使用nginx实现基于一次性cookie的简单身份验证

19

我有一个网站,仅供 3 位程序员私人消费。它是简单的 HTML 直接由 nginx 提供,但旨在在办公室内外使用。

我想要一个简单的密码或认证机制。我可以使用 HTTP 认证,但这些往往会经常过期,这让人们使用起来很麻烦。我也担心它比 cookie 更容易被人嗅探。

所以我想知道是否可以在 JavaScript 中在他们的浏览器上设置一个带有唯一长 ID 的 cookie,并告诉 nginx 只接受具有此 cookie 的请求(针对特定子域名)。

这个方案足够简单吗?我该如何

  1. 告诉nginx按cookie过滤
  2. 在浏览器中设置永不过期的cookie?
2个回答

26

我从Christian Stocker的博客文章中发现了一个看起来非常简单的解决方案。它实现了以下规则:

  1. 如果用户位于内部IP,则允许访问。
  2. 如果用户设置了cookie,则允许访问。
  3. 如果两者都不匹配,则向用户提供基本的HTTP验证,并如果他们成功地进行认证,则设置长期的cookie

这真的是最好的两个世界的结合。

这是配置:

map $cookie_letmein $mysite_hascookie {
  "someRandomValue" "yes";
  default           "no";
}
 
geo $mysite_geo {
  192.168.0.0/24 "yes"; #some network which should have access
  10.10.10.0/24  "yes"; #some other network which should have access
  default        "no";
}
 
 
map $mysite_hascookie$mysite_geo $mysite_authentication{
  "yesyes" "off";  #both cookie and IP are correct  => OK
  "yesno"  "off"; #cookie is ok, but IP not  => OK
  "noyes"  "off";  #cookie is not ok, but IP is ok => OK
  default  "Your credentials please"; #everythingles => NOT OK
}
 
server {
  listen 80;
  server_name mysite.example.org;
  location / {
    auth_basic  $mysite_authentication;
    auth_basic_user_file  htpasswd/mysite;
    add_header Set-Cookie "letmein=someRandomValue;max-age=3153600000;path=/"; #set that special cookie, when everything is ok
    proxy_pass http://127.0.0.1:8000/;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

第7行和第8行应该使用分号而不是冒号。 - aag
这是一个很好的方法,但是这种特定的做法有一些小缺陷(你不能拥有每个服务器的cookie,所写的cookie本身不适合作为auth cookie(尽管它显然是作为一个例子),如果你继续访问网站,cookie将永远不会过期(也许这是一个功能?)。我有一个稍微改进过的版本,你可以在https://gist.github.com/eatnumber1/92e94086dafc7194077df4a6b45b2b75找到。 - eatnumber1

15

如果要让Nginx按cookie进行过滤,您可以在cookie不存在时执行某些操作,然后为那三个有权限的人执行真正的操作,例如:

server {
    listen 443 ssl http2;  # Use HTTPS to protect the secret
    ...
    if ($http_cookie !~ 'secretvalue') {
        return 401; 
    }
    location / {
       # Auth'd behaviour goes here
    }
}

要设置一个永不过期的cookie,请在您的服务器主机名上的页面上启动浏览器的JavaScript控制台,并输入:

document.cookie = 'cookie=secretvalue;max-age=3153600000;path=/;secure';

技术上来说这并不是永久的,但100年应该足够了。如果你想的话也可以使用expires=以RFC1123格式设置绝对日期,并且如果需要的话可以轻松调整path。这也会在cookie上设置secure标志,因此只有通过HTTPS才能发送。

还有一些浏览器插件可以让你创建任意的cookie,但现代浏览器都有JavaScript控制台。


1
我想你的意思是!~。 - doron aviguy
好的发现,@doronaviguy。已更新答案! - davidjb
2
如果Cookie以明文形式发送,这将泄露秘密值到公共互联网中,这似乎不太好。我认为使用TLS就足以保护秘密了,对吧?你还面临跨站攻击的风险,对吧? - Daniel King
1
@DanielKing 更新了示例以使用HTTPS和安全Cookie。一般来说,它是一个Cookie,因此网络传输和跨域访问都适用于标准Cookie处理/保护。如果您的页面存在XSS,则使用DOM可访问的Cookie会有麻烦,但通过一些额外的服务器端工作,您可以实现HttpOnly cookies - davidjb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接