node.js로 보안 REST API를 구현하는 방법
나는 node.js, express, mongodb로 REST API 계획을 시작합니다.API는 웹 사이트(공용 및 개인 영역)와 나중에 모바일 앱에 대한 데이터를 제공합니다.프론트 엔드는 Angular로 개발됩니다.제이에스
며칠 동안 REST API 보안에 대해 많이 읽었지만 최종 해결책에 도달하지 못했습니다.제가 알기로는 HTTPS를 사용하여 기본적인 보안을 제공하는 것으로 알고 있습니다.그러나 사용 사례에서 API를 보호하는 방법은 다음과 같습니다.
웹 사이트/앱의 방문자/사용자만 웹 사이트/앱의 공용 영역에 대한 데이터를 가져올 수 있습니다.
인증되고 권한이 부여된 사용자만 개인 영역(사용자가 권한을 부여한 데이터만)에 대한 데이터를 가져올 수 있습니다.
현재는 활성 세션을 가진 사용자만 API를 사용할 수 있도록 허용할 생각입니다.사용자에게 권한을 부여하기 위해서는 여권을 사용할 것이며 허가를 위해서는 나 자신을 위한 무언가를 실행해야 합니다.모두 HTTPS 상단에 있습니다.
누군가 베스트 프랙티스나 경험을 제공할 수 있습니까?제 "아키텍처"에 부족한 점이 있습니까?
당신이 묘사한 것과 같은 문제를 겪었습니다.제가 구축하고 있는 웹 사이트는 휴대폰과 브라우저에서 액세스할 수 있기 때문에 사용자가 등록하고 로그인하고 특정 작업을 수행할 수 있는 API가 필요합니다.또한 다른 프로세스/머신에서 실행되는 동일한 코드인 확장성을 지원해야 합니다.
사용자는 리소스(POST/PUT 작업)를 생성할 수 있으므로 api를 보호해야 합니다.oauthor를 사용하거나 직접 솔루션을 구축할 수 있지만 암호를 검색하기가 정말 쉬운 경우 모든 솔루션이 손상될 수 있습니다.기본 아이디어는 사용자 이름, 암호 및 토큰(아피토큰)을 사용하여 사용자를 인증하는 것입니다.이 pitoken은 node-uuid를 사용하여 생성할 수 있으며 암호는 pbkdf2를 사용하여 해시할 수 있습니다.
그런 다음 세션을 어딘가에 저장해야 합니다.일반 개체의 메모리에 저장하면 서버를 종료하고 다시 재부팅하면 세션이 삭제됩니다.또한 이것은 확장 가능하지 않습니다.haproxy를 사용하여 시스템 간 로드 밸런싱을 수행하거나 단순히 작업자를 사용하는 경우 이 세션 상태가 단일 프로세스에 저장되므로 동일한 사용자가 다른 프로세스/시스템으로 리디렉션되면 다시 인증해야 합니다.따라서 세션을 공용 위치에 저장해야 합니다.이 작업은 일반적으로 redis를 사용하여 수행됩니다.
사용자가 인증되면(사용자 이름+암호+아피토큰) 세션에 대한 다른 토큰(액세스 토큰)을 생성합니다.다시 node-uuid를 사용합니다.사용자에게 액세스 토큰과 사용자 ID를 보냅니다.사용자 ID(키) 및 액세스 토큰(값)은 redis 및 만료 시간(예: 1시간)에 저장됩니다.
이제 사용자가 rest api를 사용하여 작업을 수행할 때마다 사용자 ID와 액세스 토큰을 보내야 합니다.
사용자가 rest api를 사용하여 가입할 수 있도록 허용하는 경우, 신규 사용자는 가입 시 apitoken이 없으므로 admin apitoken으로 관리자 계정을 생성하여 모바일 앱(암호화된 사용자 이름+password+apitoken)에 저장해야 합니다.
웹에서도 이 API를 사용하지만 당신은 피토켄을 사용할 필요가 없습니다.redispore와 함께 express를 사용하거나 위에서 설명한 것과 동일한 방법을 사용할 수 있지만 apitoken 검사를 무시하고 쿠키에 있는 userid+accessstoken을 사용자에게 반환할 수 있습니다.
개인 영역이 있는 경우 사용자 이름을 인증할 때 허용된 사용자와 비교합니다.사용자에게 역할을 적용할 수도 있습니다.
요약:.
피토큰이 없는 다른 방법은 HTTPS를 사용하여 Authorization 헤더에 사용자 이름과 암호를 보내고 사용자 이름을 redis에 캐시하는 것입니다.
저는 이 코드를 수용된 답변에 따라 (그렇게 되길 바랍니다) 제기된 질문에 대한 구조적인 해결책으로 기여하고 싶습니다. (매우 쉽게 사용자 정의할 수 있습니다.)
// ------------------------------------------------------
// server.js
// .......................................................
// requires
var fs = require('fs');
var express = require('express');
var myBusinessLogic = require('../businessLogic/businessLogic.js');
// .......................................................
// security options
/*
1. Generate a self-signed certificate-key pair
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out certificate.pem
2. Import them to a keystore (some programs use a keystore)
keytool -importcert -file certificate.pem -keystore my.keystore
*/
var securityOptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('certificate.pem'),
requestCert: true
};
// .......................................................
// create the secure server (HTTPS)
var app = express();
var secureServer = require('https').createServer(securityOptions, app);
// ------------------------------------------------------
// helper functions for auth
// .............................................
// true if req == GET /login
function isGETLogin (req) {
if (req.path != "/login") { return false; }
if ( req.method != "GET" ) { return false; }
return true;
} // ()
// .............................................
// your auth policy here:
// true if req does have permissions
// (you may check here permissions and roles
// allowed to access the REST action depending
// on the URI being accessed)
function reqHasPermission (req) {
// decode req.accessToken, extract
// supposed fields there: userId:roleId:expiryTime
// and check them
// for the moment we do a very rigorous check
if (req.headers.accessToken != "you-are-welcome") {
return false;
}
return true;
} // ()
// ------------------------------------------------------
// install a function to transparently perform the auth check
// of incoming request, BEFORE they are actually invoked
app.use (function(req, res, next) {
if (! isGETLogin (req) ) {
if (! reqHasPermission (req) ){
res.writeHead(401); // unauthorized
res.end();
return; // don't call next()
}
} else {
console.log (" * is a login request ");
}
next(); // continue processing the request
});
// ------------------------------------------------------
// copy everything in the req body to req.body
app.use (function(req, res, next) {
var data='';
req.setEncoding('utf8');
req.on('data', function(chunk) {
data += chunk;
});
req.on('end', function() {
req.body = data;
next();
});
});
// ------------------------------------------------------
// REST requests
// ------------------------------------------------------
// .......................................................
// authenticating method
// GET /login?user=xxx&password=yyy
app.get('/login', function(req, res){
var user = req.query.user;
var password = req.query.password;
// rigorous auth check of user-passwrod
if (user != "foobar" || password != "1234") {
res.writeHead(403); // forbidden
} else {
// OK: create an access token with fields user, role and expiry time, hash it
// and put it on a response header field
res.setHeader ('accessToken', "you-are-welcome");
res.writeHead(200);
}
res.end();
});
// .......................................................
// "regular" methods (just an example)
// newBook()
// PUT /book
app.put('/book', function (req,res){
var bookData = JSON.parse (req.body);
myBusinessLogic.newBook(bookData, function (err) {
if (err) {
res.writeHead(409);
res.end();
return;
}
// no error:
res.writeHead(200);
res.end();
});
});
// .......................................................
// "main()"
secureServer.listen (8081);
이 서버는 컬로 테스트할 수 있습니다.
echo "---- first: do login "
curl -v "https://localhost:8081/login?user=foobar&password=1234" --cacert certificate.pem
# now, in a real case, you should copy the accessToken received before, in the following request
echo "---- new book"
curl -X POST -d '{"id": "12341324", "author": "Herman Melville", "title": "Moby-Dick"}' "https://localhost:8081/book" --cacert certificate.pem --header "accessToken: you-are-welcome"
저는 이것을 꽤 기본적이지만 명확한 방법으로 수행하는 샘플 앱을 방금 마쳤습니다.mongoodb와 함께 mongoose를 사용하여 인증 관리를 위한 사용자 및 여권을 저장합니다.
https://github.com/Khelldar/Angular-Express-Train-Seed
여기 SO에 대한 REST 인증 패턴에 대한 질문이 많습니다.다음은 귀하의 질문과 가장 관련이 있습니다.
기본적으로 API 키 사용(권한 없는 사용자가 키를 검색할 수 있으므로 가장 안전하지 않음), 앱 키 및 토큰 콤보(중간) 또는 전체 OAuth 구현(가장 안전함) 중 하나를 선택해야 합니다.
응용프로그램을 보호하려면 HTTP 대신 HTTPS를 사용하는 것으로 시작해야 합니다. 이렇게 하면 사용자와 사용자 사이에 안전한 채널을 만들어 사용자에게 주고 받는 데이터의 스니핑을 방지하고 데이터를 기밀로 교환하는 데 도움이 됩니다.
JWT(JSON Web Tokens)를 사용하여 RESTful API를 보호할 수 있으며, 서버 측 세션과 비교하여 다음과 같은 많은 이점이 있습니다.
1 - API 서버가 각 사용자의 세션을 유지 관리할 필요가 없으므로 확장성이 향상됩니다(세션이 많을 경우 큰 부담이 될 수 있음).
2- JWT는 자체적으로 포함되어 있으며, 예를 들어 사용자 역할을 정의하는 클레임이 있으며 액세스할 수 있는 항목을 포함하여 날짜 및 만료 날짜에 발급됩니다(이후 JWT는 유효하지 않음).
3 - 로드 밸런서 간에 처리가 용이하며 세션 데이터를 공유하거나 세션을 동일한 서버로 라우팅하도록 서버를 구성할 필요가 없으므로 여러 API 서버를 보유한 경우 JWT의 요청이 서버에 도달할 때마다 인증 및 인증이 가능합니다.
4 - 각 요청에 대한 세션 ID 및 데이터를 지속적으로 저장 및 검색할 필요가 없을 뿐만 아니라 DB에 대한 부담 감소
5 - 강력한 키를 사용하여 JWT에 서명하면 JWT를 조작할 수 없으므로 사용자 세션 및 승인 여부를 확인할 필요 없이 요청과 함께 전송된 JWT의 클레임을 신뢰할 수 있습니다. JWT를 확인하면 사용자가 누구와 무엇을 할 수 있는지 모두 알 수 있습니다.
많은 라이브러리는 대부분의 프로그래밍 언어에서 JWT를 생성하고 검증하는 쉬운 방법을 제공합니다. 예를 들어 node.js에서 가장 인기 있는 것 중 하나는 jsonwebtoken입니다.
그러나 REST API는 일반적으로 서버를 상태 비저장 상태로 유지하는 것을 목표로 하기 때문에 JWT는 서버가 사용자와 역할을 기억할 수 있도록 상태를 유지하는 세션에 비해 사용자 세션을 추적할 필요 없이 각 요청이 자체 포함된 권한 부여 토큰(JWT)과 함께 전송되므로 해당 개념과 더 호환됩니다.세션은 또한 널리 사용되며 원하는 경우 검색할 수 있는 장점이 있습니다.
한 가지 중요한 점은 HTTPS를 사용하여 JWT를 클라이언트에 안전하게 전달하고 안전한 장소(예: 로컬 스토리지)에 저장해야 한다는 것입니다.
회사의 관리자만 액세스할 수 있는 웹 응용프로그램의 완전히 잠긴 영역을 원하는 경우 SSL 인증을 사용할 수 있습니다.브라우저에 인증된 인증서가 설치되어 있지 않으면 누구도 서버 인스턴스에 연결할 수 없습니다.지난 주에 저는 서버를 설정하는 방법에 대한 기사를 썼습니다.기사
사용자 중 한 명이 잠재적인 해커에게 키 파일을 넘겨주지 않는 한 아무도 액세스할 수 없도록 사용자 이름/비밀번호가 포함되어 있지 않기 때문에 이 설정은 가장 안전한 설정 중 하나입니다.
언급URL : https://stackoverflow.com/questions/15496915/how-to-implement-a-secure-rest-api-with-node-js
'sourcecode' 카테고리의 다른 글
메이븐 오류 "전송 실패...." (0) | 2023.05.24 |
---|---|
Git: 커밋 없이 작업 복사본으로 체리 픽 (0) | 2023.05.24 |
'break'에 해당하는 VB 키워드 (0) | 2023.05.24 |
특정 커밋으로 병합 (0) | 2023.05.24 |
MongoDB 실행 중이지만 셸을 사용하여 연결할 수 없음 (0) | 2023.05.24 |