이 글은 주로 Express 쿠키 파서 미들웨어 구현 예제에 대한 심층 분석을 소개합니다. 이제 이를 여러분과 공유하고 참고하겠습니다.
cookie-parser는 Express의 미들웨어로 쿠키 구문 분석을 구현하는 데 사용되며 공식 스캐폴딩에 내장된 미들웨어 중 하나입니다.
사용 방법은 매우 간단하지만 사용 중에 가끔 문제가 발생할 수 있습니다. 이는 일반적으로 Express + 쿠키 파서의 서명 및 확인 메커니즘에 대한 이해 부족으로 인해 발생합니다.
이 글에서는 Express + 쿠키 파서의 서명 및 확인 구현 메커니즘과 쿠키 서명이 웹사이트 보안을 강화하는 방법에 대한 심층적인 설명을 제공합니다.
텍스트 동기화는 GitHub 테마 시리즈 "Nodejs 학습 노트"에 포함되어 있습니다.
cookie-parser의 사용을 살펴보기 위해 가장 간단한 예부터 시작하겠습니다. 여기에는 구성이 사용됩니다.
쿠키 설정: Express의 내장 메소드 res.cookie() 를 사용하세요.
쿠키 구문 분석: 쿠키 파서 미들웨어를 사용합니다.
var express = require('express'); var cookieParser = require('cookie-parser'); var app = express(); app.use(cookieParser()); app.use(function (req, res, next) { console.log(req.cookies.nick); // 第二次访问,输出chyingp next(); }); app.use(function (req, res, next) { res.cookie('nick', 'chyingp'); res.end('ok'); }); app.listen(3000);
현재 시나리오에서 쿠키 파서 미들웨어는 대략 다음과 같이 구현됩니다.
app.use(function (req, res, next) { req.cookies = cookie.parse(req.headers.cookie); next(); });
보안상의 이유로 일반적으로 쿠키에 서명해야 합니다.
예제는 몇 가지 참고 사항과 함께 다음과 같이 다시 작성되었습니다.
cookieParser가 초기화되면 서명 키로 비밀을 전달합니다.
쿠키를 설정할 때 signed를 true로 설정하여 설정하려는 쿠키에 서명하세요.
쿠키를 얻을 때 req.cookies 또는 req.signedCookies를 통해 얻을 수 있습니다.
var express = require('express'); var cookieParser = require('cookie-parser'); var app = express(); // 初始化中间件,传入的第一个参数为singed secret app.use(cookieParser('secret')); app.use(function (req, res, next) { console.log(req.cookies.nick); // chyingp console.log(req.signedCookies.nick); // chyingp next(); }); app.use(function (req, res, next) { // 传入第三个参数 {signed: true},表示要对cookie进行摘要计算 res.cookie('nick', 'chyingp', {signed: true}); res.end('ok'); }); app.listen(3000);
서명 전 쿠키 값은 chyingp
이고, 서명 후 쿠키 값은 s%3Achyingp.uVofnk6k%2B9mHQpdPlQeOfjM8B5oa6mppny9d%2BmG9rD0
이며, 디코딩 후 쿠키 값은 다음과 같습니다. s :chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
. chyingp
,签名后的cookie值为 s%3Achyingp.uVofnk6k%2B9mHQpdPlQeOfjM8B5oa6mppny9d%2BmG9rD0
,decode后为 s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
。
下面就来分析下,cookie的签名、解析是如何实现的。
Express完成cookie值的签名, cookie-parser 实现签名cookie的解析。两者共用同一个秘钥。
cookie签名
Express对cookie的设置(包括签名),都是通过 res.cookie() 这个方法实现的。
精简后的代码如下:
res.cookie = function (name, value, options) { var secret = this.req.secret; var signed = opts.signed; // 如果 options.signed 为true,则对cookie进行签名 if (signed) { val = 's:' + sign(val, secret); } this.append('Set-Cookie', cookie.serialize(name, String(val), opts)); return this; };
sign 为签名函数。伪代码如下,其实就是把cookie的原始值,跟hmac后的值拼接起来。
敲黑板划重点:签名后的cookie值,包含了原始值。
function sign (val, secret) { return val + '.' + hmac(val, secret); }
这里的 secret
哪来的呢?是 cookie-parser
初始化的时候传入的。如下伪代码所示:
var cookieParser = function (secret) { return function (req, res, next) { req.secret = secret; // ... next(); }; }; app.use(cookieParser('secret'));
签名cookie解析
知道了cookie签名的机制后,如何"解析"签名cookie就很清楚了。这个阶段,中间件主要做了两件事:
将签名cookie对应的原始值提取出来
验证签名cookie是否合法
实现代码如下:
// str:签名后的cookie,比如 "s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0" // secret:秘钥,比如 "secret" function signedCookie(str, secret) { // 检查是否 s: 开头,确保只对签过名的cookie进行解析 if (str.substr(0, 2) !== 's:') { return str; } // 校验签名的值是否合法,如合法,返回true,否则,返回false var val = unsign(str.slice(2), secret); if (val !== false) { return val; } return false; }
判断、提取cookie原始值比较简单。只是是 unsign 方法名比较有迷惑性。
一般只会对签名进行合法校验,并没有所谓的反签名。
unsign
方法的代码如下:
首先,从传入的cookie值中,分别提取出原始值A1、签名值B1。
其次,用同样的秘钥对A1进行签名,得到A2。
最后,根据A2、B1是否相等,判断签名是否合法。
exports.unsign = function(val, secret){
var str = val.slice(0, val.lastIndexOf('.')) , mac = exports.sign(str, secret); return sha1(mac) == sha1(val) ? str : false; };
主要是出于安全考虑, 防止cookie被篡改 ,增强安全性。
举个小例子来看下cookie签名是如何实现防篡改的。
基于前面的例子展开。假设网站通过 nick 这个cookie来区分当前登录的用户是谁。在前面例子中,登录用户的cookie中,nick对应的值如下:(decode后的)
s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
此时,有人试图修改这个cookie值,来达到伪造身份的目的。比如修改成 xiaoming :
s:xiaoming.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
当网站收到请求,对签名cookie进行解析,发现签名验证不通过。由此可判断,cookie是伪造的。
hmac("xiaoming", "secret") !== "uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0"
비밀
은 어디서 나오는 걸까요? cookie-parser
가 초기화될 때 전달됩니다. 다음 의사 코드에 표시된 대로: 🎜rrreee🎜🎜서명된 쿠키 구문 분석🎜🎜🎜쿠키 서명 메커니즘을 알고 나면 서명된 쿠키를 "파싱"하는 방법이 분명해집니다. 이 단계에서 미들웨어는 주로 두 가지 작업을 수행합니다. 🎜🎜🎜🎜서명된 쿠키에 해당하는 원래 값을 추출합니다. 🎜🎜🎜🎜서명된 쿠키가 합법적인지 확인합니다. 🎜🎜🎜🎜구현 코드는 다음과 같습니다. 🎜rrreee🎜Judge 쿠키의 원래 값을 추출합니다. 값은 비교적 간단합니다. 단지 unsign 메소드의 이름이 혼란스러울 뿐입니다. 🎜🎜일반적으로 서명은 법적으로 확인만 되며 소위 연대서명은 없습니다. 🎜🎜unsign
메소드의 코드는 다음과 같습니다. 🎜🎜🎜🎜먼저, 들어오는 쿠키 값에서 원래 값 A1과 서명 값 B1을 추출합니다. 🎜🎜🎜🎜둘째, 동일한 비밀 키를 사용하여 A1에 서명하여 A2를 얻습니다. 🎜🎜🎜🎜마지막으로 A2와 B1이 동일한지 여부에 따라 서명이 적법한지 여부를 판단합니다. 🎜🎜🎜🎜exports.unsign = function(val, secret){🎜rrreee🎜🎜🎜쿠키 서명의 역할🎜🎜🎜🎜은 주로 보안상의 이유로 쿠키가 변조되는 것을 방지하고 보안을 강화하는 것입니다. 🎜🎜 쿠키 서명이 어떻게 변조를 방지할 수 있는지 간단한 예를 들어 보겠습니다. 🎜🎜이전 예시를 토대로 확장해 보세요. 웹사이트가 현재 로그인한 사용자가 누구인지 구별하기 위해 닉 쿠키를 사용한다고 가정합니다. 이전 예에서 로그인한 사용자의 쿠키에서 nick에 해당하는 값은 다음과 같습니다. (디코딩 후)🎜🎜s:chyingp.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
🎜🎜이에 이때 누군가가 신원 위조 목적을 달성하기 위해 이 쿠키 값을 수정하려고 시도했습니다. 예를 들어, 이를 Xiaoming으로 변경합니다: 🎜🎜s:xiaoming.uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0
🎜🎜웹사이트가 요청을 받으면 서명 쿠키를 구문 분석하여 서명 확인에 실패했음을 확인합니다. 이를 통해 쿠키가 위조된 것으로 판단할 수 있습니다. 🎜🎜hmac("xiaoming", "secret") !== "uVofnk6k+9mHQpdPlQeOfjM8B5oa6mppny9d+mG9rD0"
🎜🎜🎜🎜서명으로 보안이 보장되는 것은 아닌가요? 🎜이전 섹션의 예에서는 로그인한 사용자를 확인하기 위해 닉 쿠키 값만 사용합니다. 이는 매우 나쁜 디자인입니다. 비밀 키를 알 수 없는 경우 서명된 쿠키를 위조하는 것은 어렵습니다. 그러나 사용자 이름이 동일하면 서명도 동일합니다. 이 경우 실제로 위조가 매우 쉽습니다.
위 내용은 모두를 위해 제가 정리한 내용입니다. 앞으로 모든 사람에게 도움이 되기를 바랍니다.
관련 기사:
Vue Socket.io 소스 코드에 대한 자세한 분석
네이티브 JavaScript를 사용하여 돋보기 효과 얻기
위 내용은 Express에서 쿠키 파서 미들웨어를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!