├── .gitignore ├── README.md └── README_zh.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Understanding CSRF 3 | 4 | The Express team's [csrf](https://github.com/pillarjs/csrf) and [csurf](https://github.com/expressjs/csurf) modules 5 | frequently have issues popping up concerned about our usage of cryptographic functions. 6 | These concerns are unwarranted due to a misunderstanding of how CSRF tokens work. 7 | So here's a quick run down! 8 | 9 | Read this and still have questions? Want to tell us we're wrong? Open an issue! 10 | 11 | ## How does a CSRF attack work? 12 | 13 | On their own (phishing site), an attacker could create an AJAX button or form that creates a request against your site: 14 | 15 | ```html 16 |
17 | 18 |
19 | ``` 20 | 21 | This is worse with AJAX as the attacker could use other methods like `DELETE` as well as read the result. 22 | This is particularly important when the user has some sort of session with very personal details on your site. 23 | If this is in the context of a technologically illiterate user, 24 | there might be some inputs for credit card and social security info. 25 | 26 | ## How to mitigate CSRF attacks? 27 | 28 | ### Use only JSON APIs 29 | 30 | AJAX calls use JavaScript and are CORS-restricted. 31 | There is no way for a simple `
` to send `JSON`, 32 | so by accepting only JSON, 33 | you eliminate the possibility of the above form. 34 | 35 | ### Disable CORS 36 | 37 | The first way to mitigate CSRF attacks is to disable cross-origin requests. 38 | If you're going to allow CORS, 39 | only allow it on `OPTIONS, HEAD, GET` as they are not supposed to have side-effects. 40 | 41 | Unfortunately, this does not block the above request as it does not use JavaScript (so CORS is not applicable). 42 | 43 | ### Check the referrer header 44 | 45 | Unfortunately, checking the referrer header is a pain in the ass, 46 | but you could always block requests whose referrer headers are not from your site. 47 | This really isn't worth the trouble. 48 | 49 | For example, you could not load sessions if the referrer header is not your server. 50 | 51 | ### GET should not have side effects 52 | 53 | Make sure that none of your `GET` requests change any relevant data in your database. 54 | This is a very novice mistake to make and makes your app susceptible to more than just CSRF attacks. 55 | 56 | ### Avoid using POST 57 | 58 | Because ``s can only `GET` and `POST`, 59 | by using other methods like `PUT`, `PATCH`, and `DELETE`, 60 | an attacker has fewer methods to attack your site. 61 | 62 | ### Don't use method override! 63 | 64 | Many applications use [method-override](https://github.com/expressjs/method-override) to use 65 | `PUT`, `PATCH`, and `DELETE` requests over a regular form. 66 | This, however, converts requests that were previously invulnerable vulnerable! 67 | 68 | Don't use `method-override` in your apps - just use AJAX! 69 | 70 | ### Don't support old browsers 71 | 72 | Old browsers do not support CORS or security policies. 73 | By disabling support for older browsers 74 | (which more technologically-illiterate people use, who are more (easily) attacked), 75 | you minimize CSRF attack vectors. 76 | 77 | ### CSRF Tokens 78 | 79 | Alas, the final solution is using CSRF tokens. 80 | How do CSRF tokens work? 81 | 82 | 1. Server sends the client a token. 83 | 2. Client submits a form with the token. 84 | 3. The server rejects the request if the token is invalid. 85 | 86 | An attacker would have to somehow get the CSRF token from your site, 87 | and they would have to use JavaScript to do so. 88 | Thus, if your site does not support CORS, 89 | then there's no way for the attacker to get the CSRF token, 90 | eliminating the threat. 91 | 92 | __Make sure CSRF tokens can not be accessed with AJAX!__ 93 | Don't create a `/csrf` route just to grab a token, 94 | and especially don't support CORS on that route! 95 | 96 | The token just needs to be "unguessable", 97 | making it difficult for an attacker to successfully guess within a couple of tries. 98 | It does not have to be cryptographically secure. 99 | An attack is one or two clicks by an unbeknownst user, 100 | not a brute force attack by a server. 101 | 102 | ## BREACH attack 103 | 104 | This is where the salt comes along. 105 | The BREACH attack is pretty simple: if the server sends the same or very similar response over `HTTPS+gzip` multiple times, 106 | an attacker could guess the contents of response body (making HTTPS utterly useless). 107 | Solution? Make each response a tiny bit different. 108 | 109 | Thus, CSRF tokens are generated on a per-request basis and different every time. 110 | But the server needs to know that any token included with a request is valid. 111 | Thus: 112 | 113 | 1. Cryptographically secure CSRF tokens are now the CSRF "secret", (supposedly) only known by the server. 114 | 2. CSRF tokens are now a hash of the secret and a salt. 115 | 116 | Read more here: 117 | 118 | - [BREACH][1] 119 | - [CRIME](http://en.wikipedia.org/wiki/CRIME) 120 | - [Defending against the BREACH Attack](https://community.qualys.com/blogs/securitylabs/2013/08/07/defending-against-the-breach-attack) 121 | 122 | [1]: http://en.wikipedia.org/wiki/BREACH_(security_exploit) 123 | 124 | Note that CSRF doesn't _solve_ the BREACH attack, 125 | but the module simply randomizes requests to mitigate the BREACH attack for you. 126 | 127 | ## The salt doesn't have to be cryptographically secure 128 | 129 | __Because the client knows the salt!!!__ 130 | The server will send `;` and the client will return the same value to the server on a request. 131 | The server will then check to make sure `+=`. 132 | The salt must be sent with the token, 133 | otherwise the server can't verify the authenticity of the token. 134 | 135 | This is the simplest cryptographic method. 136 | There are more methods, but they are more complex and not worth the trouble. 137 | 138 | ## Creating tokens must be fast 139 | 140 | __Because they are created on every request!__ 141 | Doing something as simple as `Math.random().toString(36).slice(2)` is sufficient as well as extremely performant! 142 | You don't need OpenSSL to create cryptographically secure tokens on every request. 143 | 144 | ## The secret doesn't have to be secret 145 | 146 | But it is. 147 | If you're using a database-backed session store, 148 | the client will never know the secret as it's stored on your DB. 149 | If you're using cookie sessions, 150 | the secret will be stored as a cookie and sent to the client. 151 | Thus, __make sure cookie sessions use `httpOnly` so the client can't read the secret via client-side JavaScript!__ 152 | 153 | ## When you're using CSRF tokens incorrectly 154 | 155 | ### Adding them to JSON AJAX calls 156 | 157 | As noted above, if you don't support CORS and your APIs are strictly JSON, 158 | there is absolutely no point in adding CSRF tokens to your AJAX calls. 159 | 160 | ### Exposing your CSRF token via AJAX 161 | 162 | Don't ever create a `GET /csrf` route in your app 163 | and especially don't enable CORS on it. 164 | Don't send CSRF tokens with API response bodies. 165 | 166 | ## Conclusion 167 | 168 | As the web moves towards JSON APIs and browsers become more secure with more security policies, 169 | CSRF is becoming less of a concern. 170 | Block older browsers from accessing your site and change as many of your APIs to be JSON APIs, 171 | and you basically no longer need CSRF tokens. 172 | But to be safe, you should still enable them whenever possible and especially when it's non-trivial to implement. 173 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | 2 | # 理解CSRF(跨站请求伪造) 3 | >原文出处[Understanding CSRF](https://github.com/pillarjs/understanding-csrf) 4 | 5 | 对于Express团队的[csrf](https://github.com/pillarjs/csrf)模块和[csurf](https://github.com/pillarjs/csurf)模块的加密函数的用法我们经常有一些在意。 6 | 这些在意是莫须有的,因为他们不了解CSRF token是如何工作的。 7 | 下面快速过一遍! 8 | 9 | 读过后还有疑问?希望告诉我们错误?请开一个issue! 10 | 11 | ## 一个CSRF攻击是如何工作的? 12 | 13 | 在他们的钓鱼站点,攻击者可以通过创建一个AJAX按钮或者表单来针对你的网站创建一个请求: 14 | 15 | ```html 16 | 17 | 18 | 19 | ``` 20 | 21 | 这是很危险的,因为攻击者可以使用其他http方法例如 `delete` 来获取结果。 22 | 这在用户的session中有很多关于你的网站的详细信息时是相当危险的。 23 | 如果一个不懂技术的用户遇到了,他们就有可能会输入信用卡号或者个人安全信息。 24 | 25 | ## 如果减轻CSRF攻击? 26 | 27 | ### 只使用JSON api 28 | 29 | 使用JavaScript发起AJAX请求是限制跨域的。 30 | 不能通过一个简单的`
`来发送`JSON`, 31 | 所以,通过只接收JSON,你可以降低发生上面那种情况的可能性。 32 | 33 | ### 禁用CORS 34 | 35 | 第一种减轻CSRF攻击的方法是禁用cross-origin requests(跨域请求)。 36 | 如果你希望允许跨域请求,那么请只允许 `OPTIONS, HEAD, GET` 方法,因为他们没有副作用。 37 | 38 | 不幸的是,这不会阻止上面的请求由于它没有使用JavaScript(因此CORS不适用)。 39 | 40 | ### 检验referrer头部 41 | 42 | 不幸的是,检验referrer头部很麻烦, 43 | 但是你可以阻止那些referrer头部不是来自你的页面的请求。 44 | 这实在不值得麻烦。 45 | 46 | 举个例子,你不能加载session如果这个请求的referrer头部不是你的服务器。 47 | 48 | ### GET总是幂等的 49 | 50 | 确保你的`GET`请求不会修改你数据库中的相关数据。 51 | 这是一个初学者常犯的错误,使得你的应用不仅是易于遭受CSRF攻击。 52 | 53 | ### 避免使用POST 54 | 55 | 因为``只能用`GET`或是`POST`, 56 | 而不能使用别的方法,例如`PUT`, `PATCH`, `DELETE`, 57 | 攻击者很难有方法攻击你的网站。 58 | 59 | ### 不要复写方法 60 | 61 | 许多应用程序使用[复写方法](https://github.com/expressjs/method-override)来在一个常规表单中使用`PUT`, `PATCH`, 和`DELETE`请求。 62 | 这会使得原先不易受攻击的方法变得易受攻击。 63 | 64 | ### 不要兼容旧浏览器 65 | 66 | 旧的浏览器不支持CORS或是其他安全政策。 67 | 通过不兼容旧浏览器 68 | (那些不懂技术的人用的越多,我们越容易被攻击), 69 | 你可以最小化受到攻击的可能性。 70 | 71 | ### CSRF Tokens 72 | 73 | 最终的解决办法是使用CSRF tokens。 74 | CSRF tokens是如何工作的呢? 75 | 76 | 1. 服务器发送给客户端一个token。 77 | 2. 客户端提交的表单中带着这个token。 78 | 3. 如果这个token不合法,那么服务器拒绝这个请求。 79 | 80 | 攻击者需要通过某种手段获取你站点的CSRF token, 81 | 他们只能使用JavaScript来做。 82 | 所以,如果你的站点不支持CORS, 83 | 那么他们就没有办法来获取CSRF token, 84 | 降低了威胁。 85 | 86 | __确保CSRF token不能通过AJAX访问到!__ 87 | 不要创建一个`/CSRF`路由来获取一个token, 88 | 尤其不要在这个路由上支持CORS! 89 | 90 | token需要是不容易被猜到的, 91 | 让它很难被攻击者尝试几次得到。 92 | 它不需要是密码安全的。 93 | 攻击来自从一个未知的用户的一次或者两次的点击, 94 | 而不是来自一台服务器的暴力攻击。 95 | 96 | ## BREACH攻击 97 | 98 | 这也就是salt(加盐)出现的原因。 99 | Breach攻击相当简单:如果服务器通过`HTTPS+gzip`多次发送相同或者相似的响应,攻击者就可以猜测响应的内容(使得HTTPS完全无用)。 100 | 解决办法?让每一个响应都有那么一点不同。 101 | 于是,CSRF tokens依据每一个不同的请求还有不同的时间来生成。 102 | 但是服务器需要知道客户端请求中带的token是否是合法的。 103 | 因此: 104 | 105 | 1. 一般认为安全加密的CSRF tokens是防护CSRF的关键 106 | 2. CSRF tokens现在通常是一个秘钥或者salt的hash 107 | 108 | 109 | 了解更多: 110 | 111 | - [BREACH][1] 112 | - [CRIME](http://en.wikipedia.org/wiki/CRIME) 113 | - [Defending against the BREACH Attack](https://community.qualys.com/blogs/securitylabs/2013/08/07/defending-against-the-breach-attack) 114 | 115 | [1]: http://en.wikipedia.org/wiki/BREACH_(security_exploit) 116 | 117 | 注意,CSRF没有_解决_BREACH攻击, 118 | 但是这个模块通过随机化请求来为你减轻BREACH攻击。 119 | 120 | ## salt不需要加密 121 | 122 | __因为客户端知道salt!!!__ 123 | 服务器会发送 `;` ,然后客户端会通过请求返回相同的值给服务器。服务器然后会检验 `+=` 。 124 | salt必须跟token一起被发送给服务器,否则服务器不能验证这个token。 125 | 这是最简单的加密方式。 126 | 还有很多方法,不过他们更加复杂,犯不着那么麻烦。 127 | 128 | ## 创建tokens必须要快 129 | 130 | __因为每当进来一个请求他们就会被创建!__ 131 | 像`Math.random().toString(36).slice(2)`这么做也是性能足够好的! 132 | 你不需要OpenSSL来为每一个请求创建一个密码安全的token。 133 | 134 | ## 秘钥不需要是加密的,但需要是安全的 135 | 136 | 如果你正在使用一个数据库后端来存储session,客户端是不会知道秘钥的,因为它被存储在数据库中。 137 | 如果你正在使用cookie来存储session,那么秘钥就会被存储在cookie中发送给客户端。 138 | 因此, __确保cookie sessions 使用 `httpOnly` 那样客户端就不能通过客户端JavaScript来读取到秘钥!__ 139 | 140 | ## 当你不正确的使用CSRF token 141 | 142 | ### 把它们加到JSON AJAX调用中 143 | 144 | 正如上面提到的,如果你不支持CORS并且你的API是传输的严格的JSON, 145 | 绝没可能在你的AJAX 调用中加入CSRF token。 146 | 147 | ### 通过AJAX暴露你的CSRF token 148 | 149 | 不要创建一个`GET /csrf`路由 150 | 并且尤其不要在这个路由上支持CORS。 151 | 不要发送CSRF token在API响应的body中。 152 | 153 | ## 结论 154 | 155 | 因为web正在向JSON API转移,并且浏览器变得更安全,有更多的安全策略, 156 | CSRF正在变得不那么值得关注。 157 | 阻止旧的浏览器访问你的站点,并尽可能的将你的API变成JSON API, 158 | 然后你将不再需要CSRF token。 159 | 但是为了安全起见,你还是应该尽量允许他们尤其是当难以实现的时候。 160 | --------------------------------------------------------------------------------