-
-
Save perusio/5017911 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
server { | |
listen 80; | |
root /root/to/your/docroot; | |
proxy_redirect off; | |
proxy_intercept_errors on; | |
proxy_set_header Host $host; | |
proxy_set_header X-Real-IP $remote_addr; | |
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
proxy_set_header X-Forwarded-Proto $scheme; | |
proxy_set_header Request-URI $request_uri; | |
proxy_set_header X-Backend example; | |
proxy_pass_header Set-Cookie; | |
location ~ \..*/.*\.php$ { | |
return 403; | |
} | |
location / { | |
# This is cool because no php is touched for static content | |
expires max; | |
try_files $uri @backend; | |
} | |
location /validate-csrf { | |
# Only accessible as a subrequest | |
internal; | |
content_by_lua | |
' | |
if ngx.var.cookie_csrf then | |
-- user has a CSRF cookie, validate it | |
local redis = require "redis" | |
local client = redis.connect("127.0.0.1", 6379) | |
local csrf_cookie = "csrf_" .. ngx.var.cookie_csrf | |
local value = client:get(csrf_cookie) | |
if value then | |
ngx.say(value) -- useful for testing | |
return ngx.exit(ngx.HTTP_OK) | |
end | |
end | |
-- no cookie or csrf provided was not found | |
return ngx.exit(ngx.HTTP_NOT_FOUND) | |
'; | |
} | |
location @backend { | |
# You can't set variables in nginx dynamically, so set this up as empty first | |
set $csrf_validate ""; | |
access_by_lua | |
' | |
if ngx.req.get_method() == "POST" then | |
-- set up forbidden as default | |
ngx.var.csrf_validate = ngx.HTTP_FORBIDDEN | |
if ngx.var.cookie_csrf then | |
local res = ngx.location.capture("/validate-csrf") | |
if ngx.HTTP_OK == res.status then | |
ngx.req.read_body() | |
local args = ngx.req.get_post_args() | |
local posted_token = tostring(args["csrf"]) | |
if posted_token == res.body then | |
ngx.var.csrf_validate = ngx.HTTP_OK | |
end | |
end | |
end | |
end | |
'; | |
# Pass the result as a header to the backend | |
proxy_set_header X-Csrf-Valid $csrf_validate; | |
proxy_pass http://127.0.0.1:6081; | |
# Now filter the response from the backend in as lightweight way as possible | |
set $csrf_form_token ""; | |
set $csrf_cookie_token ""; | |
header_filter_by_lua | |
' | |
if ngx.var.upstream_http_x_csrf_tokenize then | |
-- the backend requested a CSRF token be set | |
local csrf_cookie_token = nil | |
if ngx.var.cookie_csrf then | |
-- they have a cookie, just re-use it | |
local csrf_cookie_token = ngx.var.cookie_csrf | |
end | |
local resty_random = require "resty.random" | |
local str = require "resty.string" | |
if not csrf_cookie_token then | |
-- no valid csrf cookie found, let us make one | |
local cookie_random = resty_random.bytes(16,true) | |
while cookie_random == nil do | |
-- attempt to generate 16 bytes of | |
-- cryptographically strong (enough) random data | |
cookie_random = resty_random.bytes(16,true) | |
end | |
ngx.var.csrf_cookie_token = str.to_hex(cookie_random) | |
end | |
-- we are about to mess around with the content of the page | |
-- so we need to clear this as it will be wrong | |
ngx.header.Content_Length = "" | |
-- set the Cookie for the CSRF token | |
ngx.header.Set_Cookie = "csrf=" .. ngx.var.csrf_cookie_token | |
ngx.header.tokenize = ngx.var.upstream_http_x_csrf_tokenize | |
-- now generate one for the form token | |
while form_random == nil do | |
form_random = resty_random.bytes(16,true) | |
end | |
ngx.var.csrf_form_token = str.to_hex(form_random) | |
local redis = require "redis" | |
local client = redis.connect("127.0.0.1", 6379) | |
client:set("csrf_" .. ngx.var.csrf_cookie_token, ngx.var.csrf_form_token) | |
end | |
'; | |
# Parse the body for csrf placeholder(s) and replace them with a hash | |
body_filter_by_lua | |
' | |
local csrf_form_token = ngx.var.csrf_form_token or "" | |
if ngx.var.csrf_form_token then | |
local placeholder = ngx.var.upstream_http_x_csrf_tokenize or "" | |
ngx.arg[1] = ngx.re.gsub(ngx.arg[1], placeholder, ngx.var.csrf_form_token) | |
end | |
'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm amused that I was recently in awe of your Drupal nginx conf (I'm mentioning it in a talk I'm giving tomorrow) and when digging out my example of this and saw you had forked it a year ago!