hack相关知识
-
最全的CSS hack没有之一做前端快1年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况。基于此,某些情况我们会极不情愿的使用这个不太友好的方式来达到大家要求的页面表现。我个人是不太推荐使用hack的,要知道一名好的前端,要尽可能不使用hack的情况下实现需求,做到较好的用户体验。可是啊,现实太残酷,浏览器厂商之间历史遗留的问题让我们在目标需求下不得不向hack妥协,虽然这只是个别情况。今天,结合自己的经验和理解,做了几个demo把IE6~IE10和其他标准浏览器的CSS hack做一个总结,也许
-
不同浏览器兼容性——常用的CSS Hack技术集锦一、什么是CSS Hack? 不同的浏览器对CSS的解析结果是不同的,因此会导致相同的CSS输出的页面效果不同,这就需要CSS Hack来解决浏览器局部的兼容性问题。而这个针对不同的浏览器写不同的CSS 代码的过程,就叫CSS Hack。 CSS Hack常见的有三种形式:CSS属性Hack、CSS选择符Hack以及IE条件注释Hack, Hack主要针对IE
-
[干货]前端必会:CSS hack技术与浏览器兼容什么是 CSS hack 由于不同厂商的浏览器,比如Internet Explorer,Safari,Mozilla Firefox,Chrome等,或者是同一厂商的浏览器的不同版本,如IE6和IE7,对CSS的解析认识不完全一样,因此会导致生成的页面效果不一样,得不到我们所需要的页面效果。 因此,我们需要针对不同的浏览器去写不同的CSS,让它能在不同的浏览器中也能得到我们想要的页面效果。 针对不同的浏览器/不同版本写相应的CSS代码的过程,叫做CSS Hack 常用的CSS hack技术 1.属性前缀法(即类内部Hack) 例如 IE6能识别下
-
这可能是世上最全的CSS hack了,附带例子哦!在前端开发中最烦人的问题莫过于浏览器兼容性问题了,下面是我工作依赖积累的CSS hack笔记,都是再线上使用过的哦,都是经过考验的! Webkit内核CSS hack(Chrome、Safari、Opera)。 注:很奇怪,在Windows10中的Edge(版本是13)下,竟然也起作用,莫非微软要向Webkit看齐? @media screen and (-webkit-min-devi
hack相关课程
hack相关教程
- 4.4 CSS Hack Hack 类似于上边提到的特有 CSS 样式,区别在于 Hack 是针对标准的 CSS 加上特殊符号,用于被特有的浏览器识别,以下举例:1026以上举了几个代表性的 Hack 的例子,实际上花大力气解决这些兼容性问题, 无非是给各个浏览器厂商填坑罢了,开发项目中一般不推荐使用过多的 Hack ,因为这会导致代码生僻难懂,维护成本过高。
- 4.1 原因 兼容性问题是网站技术中老生常谈的问题,包括 HTML、JavaScript、CSS 都会出现兼容性问题。导致这个问题的根本原因是不同的浏览器厂商的内核不同,导致对 CSS 的解析效果不一致,继而显示效果千差万别。这里不去过分讨论内核不兼容的深层次原理,而是讨论一下大的解决思路,主要包括 4 个方面,浏览器 CSS 样式初始化、浏览器私有属性,CSS hack 语法和第三方插件。
- 3.1 注解 在工作中我们会经常用到注解功能,它是我们约定的一种注释方式,用来分类并提示在工作中,我们接下来要做的一些事情。常用的注解有下面几种:关键字什么时候使用TODO备注缺失的特性或者在以后添加的功能FIXME备注有问题需要修复的代码OPTIMIZE来备注慢的或者低效的可能引起性能问题的代码HACK备注那些使用问题代码的地方可能需要重构REVIEW来备注那些需要反复查看确认工作正常的代码。例如: REVIEW: 你确定客户端是怎样正确的完成 X 的吗?经验:注解应该写在紧接相关代码的上方;注解关键字后跟一个冒号和空格,然后是描述问题的记录;如果需要多行来描述问题,随后的行需要在 # 后面缩进两个空格;注解不应该放在行尾而没有任何备注。实例:# 错误的实例def bar sleep 100 # OPTIMIZEend实例:# 正确的实例def bar # FIXME: This has crashed occasionally since v3.2.1. It may # be related to the BarBazUtil upgrade. baz(:quux)end
- 1. eval eval可以将字符串作为代码进行执行,并返回代码的返回值。它的使用方法是eval(code_string)。code_string可以是完整的Ruby代码、或表达式。实例:p eval("1 + 1")# ---- 输出结果 ----2解释:在这里"1 + 1"是一个表达式的字符串,我们使用eval来将这个字符串当做表达式进行执行,得到返回值2.当然,如果字符串里面代码运行报错也会抛出异常。实例:eval(" puts a ")# ---- 输出结果 ----Traceback (most recent call last): 2: from ruby.rb:1:in `<main>' 1: from ruby.rb:1:in `eval'ruby.rb:1:in `eval': undefined local variable or method `a' for main:Object (NameError)让我们使用eval来定义一个类。eval <<-DEFINED_CLASS class Person def name 'Andrew' end endDEFINED_CLASSperson = Person.newp person.name# ---- 输出结果 ----"Andrew"解释:这里我们使用heredoc来定义了一个多行字符串,里面的内容是定义一个类Person,并在类里面定义了一个实例方法name。从最后输出结果来看,我们成功的创建了这个类以及对应的实例方法。Tips:eval功能非常强大,灵活度也很高,但是它很危险,有点类似于SQL注入。比如我们定义了一个方法,可以打印出我们传入的东西。实例:str = "Hi"eval %Q{ def method puts #{str} end}method# ---- 输出结果 ----Hi但是如果这个str可以被外界篡改,就会产生非常大的问题。实例:str = "'Hi'; system('touch i_am_hack && echo \"I am hack\" > i_am_hack'); system('ls');"eval %Q{ def method puts #{str} end}method# ---- 输出结果 ----Hi你当前所在的目录结构!!!!解释:当输出Hi的同时,我们创建了一个i_am_hack的文件,并且获取了执行脚本所在文件夹目录结构。所以,请轻易不要使用eval。Tips:有时,eval = evil。
- 1. 深入 Django 中 CSRF 校验过程 上一节中提到了,针对 CSRF 攻击有效的解决方案是在网页上添加一个随机的校验 token 值,我们前面的登录的模板页面中添加的 {% csrf_token %},这里正好对应着一个随机值。我们拿之前的登录表单来进行观察,会发现这样几个现象:网页上隐藏的 csrf_token 值会在每次刷新时变化;对应在请求和响应头部的 cookie 中的 csrftoken值却一直不变;这样子我们对应会产生几个思考问题:为什么网页上的 token 值会变,而 cookie 中的 token 则一直不变?整个 token 的校验过程是怎样的,有密码?如果有密码,密码存在哪里?今天我们会带着这两个问题,查看下 Django 内部源码,找到这些问题的代码位置。我可能不会很完整的描述整个代码运行的逻辑,因为篇幅不够,而且细节太多,容易迷失在代码的海洋里。首先毋庸置疑的第一步是找我们在 settings.py 中设置的 CSRF 中间件:MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]我们在上一讲中提到过中间件类的两个函数:process_request() 和 process_response()。而在 CSRF 中间件文件中还有一个方法:process_view()。中间类比较完整的处理流程示意图如下所示,可以看到中间件的 process_view() 方法如果返回 None,则会执行下一个 中间件的 process_view() 方法。一旦它返回 HttpResponse 实例,则直接跳过视图函数到达最后一个中间件的 process_response() 方法中。我们来关注下 django.middleware 目录下的 csrf.py 文件,所有的答案都在这里可以找到。首先看最核心的中间件类:# 源码位置:django/middleware/csrf.py# ...class CsrfViewMiddleware(MiddlewareMixin): def _accept(self, request): # Avoid checking the request twice by adding a custom attribute to # request. This will be relevant when both decorator and middleware # are used. request.csrf_processing_done = True return None def _reject(self, request, reason): response = _get_failure_view()(request, reason=reason) log_response( 'Forbidden (%s): %s', reason, request.path, response=response, request=request, logger=logger, ) return response def _get_token(self, request): # ... def _set_token(self, request, response): # ... def process_request(self, request): csrf_token = self._get_token(request) if csrf_token is not None: # Use same token next time. request.META['CSRF_COOKIE'] = csrf_token def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works if getattr(callback, 'csrf_exempt', False): return None # Assume that anything not defined as 'safe' by RFC7231 needs protection if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. # It comes after the creation of CSRF cookies, so that # everything else continues to work exactly the same # (e.g. cookies are sent, etc.), but before any # branches that call reject(). return self._accept(request) # 判断是不是 https 协议,不然不用执行这里 if request.is_secure(): # ... csrf_token = request.META.get('CSRF_COOKIE') if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. return self._reject(request, REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') except IOError: # Handle a broken connection before we've completed reading # the POST data. process_view shouldn't raise any # exceptions, so we'll ignore and serve the user a 403 # (assuming they're still listening, which they probably # aren't because of the error). pass if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, # and possible for PUT/DELETE. request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '') request_csrf_token = _sanitize_token(request_csrf_token) if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN) return self._accept(request) def process_response(self, request, response): if not getattr(request, 'csrf_cookie_needs_reset', False): if getattr(response, 'csrf_cookie_set', False): return response if not request.META.get("CSRF_COOKIE_USED", False): return response # Set the CSRF cookie even if it's already set, so we renew # the expiry timer. self._set_token(request, response) response.csrf_cookie_set = True return response这里比较复杂的部分就是 process_view() 方法。process_request() 方法只是从请求头中取出 csrftoken 值或者生成一个 csrftoken 值放到 request.META 属性中去;process_response() 会设置对应的 csrftoken 值到 cookie 或者 session 中去。这里获取 csrftoken 和 设置 csrftoken 调用的正是 _get_token() 和 set_token()方法:class CsrfViewMiddleware(MiddlewareMixin): # ... def _get_token(self, request): if settings.CSRF_USE_SESSIONS: try: return request.session.get(CSRF_SESSION_KEY) except AttributeError: raise ImproperlyConfigured( 'CSRF_USE_SESSIONS is enabled, but request.session is not ' 'set. SessionMiddleware must appear before CsrfViewMiddleware ' 'in MIDDLEWARE%s.' % ('_CLASSES' if settings.MIDDLEWARE is None else '') ) else: try: cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] except KeyError: return None csrf_token = _sanitize_token(cookie_token) if csrf_token != cookie_token: # Cookie token needed to be replaced; # the cookie needs to be reset. request.csrf_cookie_needs_reset = True return csrf_token def _set_token(self, request, response): if settings.CSRF_USE_SESSIONS: if request.session.get(CSRF_SESSION_KEY) != request.META['CSRF_COOKIE']: request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE'] else: response.set_cookie( settings.CSRF_COOKIE_NAME, request.META['CSRF_COOKIE'], max_age=settings.CSRF_COOKIE_AGE, domain=settings.CSRF_COOKIE_DOMAIN, path=settings.CSRF_COOKIE_PATH, secure=settings.CSRF_COOKIE_SECURE, httponly=settings.CSRF_COOKIE_HTTPONLY, samesite=settings.CSRF_COOKIE_SAMESITE, ) # Set the Vary header since content varies with the CSRF cookie. patch_vary _headers(response, ('Cookie',)) # ...如果我们没在 settings.py 中设置 CSRF_USE_SESSIONS 值时,在 django/conf/global_settings.py 默认设置为 False,那么我们就是调用前面熟悉的 response.set_cookie() 方法取设置 cookie 中的 key-value 值,也是我们在上面第二张图片所看到的 Set-Cookie 里面的值。我们来看最核心的处理方法:process_view()。它的执行流程如下所列,略有删减,请仔细研读和对照代码:判断视图方法是否有 csrf_exempt 属性。相当于该视图方法添加了 @csrf_exempt 装饰器,这样不用检验 csrf_token 值,直接返回 None,进入下面的中间件执行,直到视图函数去处理 HTTP 请求;对于 GET、HEAD、 OPTIONS、 TRACE 这四种请求不用检查 csrf_token,会直接跳到最后执行 self._accept(request) 方法。但是我们常用的如 POST、PUT 以及 DELETE 等请求会进行特别的处理;来看针对 POST、PUT 以及 DELETE 的特殊处理,要注意两处代码:request_csrf_token 值的获取:对于 POST 请求,我们要从请求参数中获取,这个值正是表单中隐藏的随机 csrf_token,也是我们在第一张图中看到的值,每次请求都会刷新该值;而且对于其它的请求,该值则是从 request.META 中获取;校验 csrf_token 值是否正确。如果是不正确的 csrf_token 值,则会直接返回 403 错误;if not _compare_salted_tokens(request_csrf_token, csrf_token): return self._reject(request, REASON_BAD_TOKEN)可以看到,这里校验的是两个值:一个是我们从 cookie 中获取的,另一个是前端表单中隐藏的那个随机数。现在我们大致心里有个数了,Django 的校验方法竟然是用 cookie 中的值和页面上的随机值进行校验,这两个值都是64位的,你必须同时拿到这两个正确 token 值才能通过 Django 的 csrf 中间件校验。比较原理,2个 token,一个放到 cookie 中,另一个放到表单中,会一直变得那种。接下来就是对这两个 token 进行对比。我们继续追踪 _compare_salted_tokens() 方法,可以在 csrf.py 中找到如下两个方法,它们分别对应着 csrf_token 值的生成和解码:# 源码位置:django/middleware/csrf.py# ...def _salt_cipher_secret(secret): """ Given a secret (assumed to be a string of CSRF_ALLOWED_CHARS), generate a token by adding a salt and using it to encrypt the secret. """ salt = _get_new_csrf_string() chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in salt)) cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs) return salt + cipherdef _unsalt_cipher_token(token): """ Given a token (assumed to be a string of CSRF_ALLOWED_CHARS, of length CSRF_TOKEN_LENGTH, and that its first half is a salt), use it to decrypt the second half to produce the original secret. """ salt = token[:CSRF_SECRET_LENGTH] token = token[CSRF_SECRET_LENGTH:] chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in salt)) secret = ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok return secret# ...来看这两个函数,首先是 _salt_cipher_secret() 方法,需要传入一个长度为 32 的 secret,就可以得到一个64位的随机字符。这个 secret 值在使用时也是随机生成的32个字符:# 源码位置:django/middleware/csrf.pydef _get_new_csrf_string(): return get_random_string(CSRF_SECRET_LENGTH, allowed_chars=CSRF_ALLOWED_CHARS)# 源码位置:django/utils/crypto.pydef get_random_string(length=12, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): """ Return a securely generated random string. The default length of 12 with the a-z, A-Z, 0-9 character set returns a 71-bit value. log_2((26+26+10)^12) =~ 71 bits """ if not using_sysrandom: # This is ugly, and a hack, but it makes things better than # the alternative of predictability. This re-seeds the PRNG # using a value that is hard for an attacker to predict, every # time a random string is required. This may change the # properties of the chosen random sequence slightly, but this # is better than absolute predictability. random.seed( hashlib.sha256( ('%s%s%s' % (random.getstate(), time.time(), settings.SECRET_KEY)).encode() ).digest() ) return ''.join(random.choice(allowed_chars) for i in range(length))在 _salt_cipher_secret() 方法中我们可以看到,传入32位的密钥 secret,最后的 csrf_token 的生成是 salt + cipher,前32位是 salt,后32位是加密字符串。解密的过程差不多就是 _salt_cipher_secret() 的逆过程了,最后得到 secret。我们可以在 Django 的 shell 模式下使用下这两个函数:(django-manual) [root@server first_django_app]# python manage.py shellPython 3.8.1 (default, Dec 24 2019, 17:04:00) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linuxType "help", "copyright", "credits" or "license" for more information.(InteractiveConsole)>>> from django.middleware.csrf import _get_new_csrf_token, _unsalt_cipher_token>>> x1 = _get_new_csrf_token()>>> x2 = _get_new_csrf_token()>>> x3 = _get_new_csrf_token()>>> print('x1={}\nx2={}\nx3={}'.format(x1, x2, x3))x1=dvK3CRLiyHJ6Xgt0B6eZ7kUjxXgZ5CKkhl8HbHq8CKR0ZXMOxYnigzDTIZIdk3xZx2=TMazqRDst3BSiyxIAI1XDiFKdbmxu8nKRVvMogERiZi6IG6KNhDSxcgEOPTqU0qFx3=gy998wPOCZJiXHo7HYQtY3dfwaevPHKAs2YXPAeJmWUaA5vV2xdXqvlidLR4XM1T>>> _unsalt_cipher_token(x1)'e0yOJ0P0edi4cRtY62jtjpTKlcCopBXP'>>> _unsalt_cipher_token(x2)'8jvn8zbzZ6RoAiJcnJM544L4LOH3A2d5'>>> _unsalt_cipher_token(x3)'mEZYRez5U7l2NyhYvJxECCidRLNJifrt'>>> 了解了上述这些方法后,现在来思考前面提出的问题:为什么每次刷新表单中的 csrf_token 值会一直变化,而 cookie 中的 csrf_token 值却一直不变呢?首先我们看在页面上生成随机 token 值的代码,也就是将标签 {{ csrf_token }} 转成 64位随机码的地方:# 源码位置: django/template/defaulttags.py@register.tagdef csrf_token(parser, token): return CsrfTokenNode()class CsrfTokenNode(Node): def render(self, context): csrf_token = context.get('csrf_token') if csrf_token: if csrf_token == 'NOTPROVIDED': return format_html("") else: return format_html('<input type="hidden" name="csrfmiddlewaretoken" value="{}">', csrf_token) else: # It's very probable that the token is missing because of # misconfiguration, so we raise a warning if settings.DEBUG: warnings.warn( "A {% csrf_token %} was used in a template, but the context " "did not provide the value. This is usually caused by not " "using RequestContext." ) return ''可以看到 csrf_token 值是从 context 中取出来的,而在 context 中的 csrf_token 值又是由如下代码生成的:# 源码位置:django/template/context_processors.pyfrom django.middleware.csrf import get_token# ...def csrf(request): """ Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if it has not been provided by either a view decorator or the middleware """ def _get_val(): token = get_token(request) if token is None: # In order to be able to provide debugging info in the # case of misconfiguration, we use a sentinel value # instead of returning an empty dict. return 'NOTPROVIDED' else: return token return {'csrf_token': SimpleLazyObject(_get_val)}可以看到,最后 csrf_token 值还是由 csrf.py 文件中的 get_token() 方法生成的。来继续看这个 get_token() 方法的代码:# 源码位置:django/middleware/csrf.pydef get_token(request): """ Return the CSRF token required for a POST form. The token is an alphanumeric value. A new token is created if one is not already set. A side effect of calling this function is to make the csrf_protect decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' header to the outgoing response. For this reason, you may need to use this function lazily, as is done by the csrf context processor. """ if "CSRF_COOKIE" not in request.META: csrf_secret = _get_new_csrf_string() request.META["CSRF_COOKIE"] = _salt_cipher_secret(csrf_secret) else: csrf_secret = _unsalt_cipher_token(request.META["CSRF_COOKIE"]) request.META["CSRF_COOKIE_USED"] = True return _salt_cipher_secret(csrf_secret)注意!注意!最关键的地方来了,这个加密的 secret 的值是从哪里来的?正是从请求头中的 cookie 信息中来的,如果没有将生成一个新的密钥,接着把该密钥生成的 token 放到 cookie 中。最后使用 _salt_cipher_secret() 方法生成的 csrf_token 和 cookie 中的 csrf_token 具有相同的密钥。同时拿到了这两个值,就可以进行校验和判断,下面我们在 ``_salt_cipher_secret()方法中加上一个print()` 语句,然后执行下看看是否如我们所说。22可以看到每次生成 token 时加密的秘钥都是一样的。我们从上面生成的 csrf_token 中选一个进行解密,得到的结果和 cookie 中的正是一样的密钥:>>> from django.middleware.csrf import _unsalt_cipher_token# 两个 token 解密是相同的,这才是正确的>>> _unsalt_cipher_token('2Tt8StiU4rZcvCrTb2KqJwTTOTCP0WvJhp7GyTj58RGv97IvJInxyrAN4DKCdt1M')'pGOIQAbleARtOFrMIQNhZ5R4qUiXnHGd'>>> _unsalt_cipher_token('VI68m6xT1JczSsnuJvxqtcr0L0EvCN1DaeKG2wy459TSwXE6hbaxi78U1KMiPkxG')'pGOIQAbleARtOFrMIQNhZ5R4qUiXnHGd'现在大部分代码我们也算清楚了,csrf_token 的校验原理我们也知道了。那么如果想自己生成 csrf_token 并通过 Django 的校验也非常简单,只需要通过那个密钥生成一个 csrf_token 或者直接输入使用密钥都可以通过校验。我们首先使用密钥在 shell 模式下随机生成一个 csrf_token 值:>>> from django.middleware.csrf import _salt_cipher_secret>>> _salt_cipher_secret('pGOIQAbleARtOFrMIQNhZ5R4qUiXnHGd')'ObaC9DEZfn4seXbOhgBCph2Y5PMjm0Eo3HOaP3FajNLLSssqPWeJecJSlzU6zxar接下来在看我的演示,第一此我随机改动 csrf_token 的字符,这样校验肯定通不过;接下来我是用自己生成的 token 值以及直接填写密钥再去提交都能通过校验。为了方便演示结果,我们在 csrf.py 的 process_view() 函数中添加几个 print() 方法,方便我们理解执行的过程。23
- Gradle 命令详解 新一代构建工具 Gradle
hack相关搜索
-
h1
h6
hack
hadoop
halt
hana
handler
hanging
hash
hashtable
haskell
hatch
hbase
hbuilder
hdfs
head
header
header php
headers
headerstyle