익스프레스로 아이디 숨기기

API를 구축할 때 우리는 종종 최종 사용자로부터 특정 정보를 숨기거나 난독화하기를 원합니다. 가장 일반적인 것은 아마도 ID를 숨기는 것입니다.

아이디를 왜 숨기나요?



제공하는 데이터 유형에 따라 ID를 숨기려는 데에는 여러 가지 이유가 있습니다.

예를 들어 API에 문서 공유(공개 Google 문서)가 포함된 경우 API에서 받은 ID를 단순히 증가시켜 사용자 파일을 열거할 수 있도록 허용하는 것은 좋지 않습니다.

또는 다른 사람들이 귀하가 보유한 사용자 또는 앱 수(또는 ID 오프셋을 사용하는 경우 시간이 지남에 따라 증가)를 확인하는 것을 원하지 않을 수 있습니다.

ID를 어떻게 숨깁니까?



성능상의 이유로 일반적으로 ID를 되돌릴 수 있기를 원하므로 ID를 직접 해싱하지 않는 것이 가장 좋습니다. 대신 사용자에게 보내기 전에 암호화하고 백엔드에서 액세스하려고 할 때 암호를 해독하려고 합니다.

이 게시물의 요점은 보안이 아니므로 hashids library 을 사용하겠습니다. 이것은 번호가 매겨진 ID에서 고유한 문자열을 생성하는 쉬운 방법을 제공합니다. Hashids는 결코 안전하지 않으며 ID를 난독화하는 간단한 방법일 뿐입니다.

코드에서 사용하는 방법은 다음과 같습니다.

const hashids = new Hashids("secret salt");

const encodedId = hashids.encode(42);
const [originalId] = hashids.decode(encodedId);


익스프레스 미들웨어



모든 공개 ID를 숨기고 싶다고 가정해 봅시다. 이는 요청 및 응답 본문의 모든 id 필드를 인코딩/디코딩한다는 의미입니다. user_id 와 같이 이것을 관계형 필드로 확장할 수도 있습니다. _id 로 끝나는 모든 필드에 동일한 작업을 수행합니다.

Express에서 이를 달성하기 위해 두 개의 미들웨어를 만들고 싶습니다. encodeMiddlewaredecodeMiddleware .

/** helper function to replace IDs inside object */
function replaceIds(obj, replaceFunc) {
  if (obj == null) return obj;

  for (const key of Object.keys(obj)) {
    if (obj[key] == null) continue;

    if (typeof obj[key] === "object")
      obj[key] = replaceIds(obj[key], replaceFunc);
    else if (key == "id" || (key.length >= 4 && key.endsWith("_id")))
      obj[key] = replaceFunc(obj[key]);
  }
  return obj;
}

function encodeMiddleware(req, res, next) {
  var _json = res.json;
  res.json = (obj) => {
    res.json = _json;
    obj = replaceIds(obj, (v) => hashids.encode(v));
    return res.json(obj);
  };
  next();
}
function decodeMiddleware(req, res, next) {
  try {
    req.query = replaceIds(req.query, (v) => hashids.decode(v)[0]);
    req.body = replaceIds(req.body, (v) => hashids.decode(v)[0]);
  } catch (e) {
    console.error(`Could not decode id:`, e);
    return res.sendStatus(404);
  }
  next();
}

encodeMiddleware에서 우리는 응답이 항상 JSON이라고 가정하고 모든 id 발생을 인코딩된 버전으로 교체하여 수정합니다.
decodeMiddleware에서 우리는 데이터가 body 또는 query에 있을 수 있고 모든 id 발생을 디코딩된 버전으로 대체할 수 있다고 가정합니다.

이 두 가지 모두에 대해 req.url 를 비교하여 특정 끝점에 예외를 추가할 수 있습니다. 아니면 전 세계적으로 미들웨어를 사용하지 않을 수도 있습니다.

또한 객체를 가져와서 제공된 함수를 사용하여 모두ids를 재귀적으로 대체하는 도우미 함수를 추가했습니다.

예제 코드



이제 모든 것이 결합된 예가 있습니다.

const express = require("express");
const Hashids = require("hashids");
const hashids = new Hashids("secret salt", 6);

/** helper function to recursively replace ids inside object */
function replaceIds(obj, replaceFunc) {
  if (obj == null) return obj;

  for (const key of Object.keys(obj)) {
    if (obj[key] == null) continue;

    if (typeof obj[key] === "object")
      obj[key] = replaceIds(obj[key], replaceFunc);
    else if (key == "id" || (key.length >= 4 && key.endsWith("_id")))
      obj[key] = replaceFunc(obj[key]);
  }
  return obj;
}

function encodeMiddleware(req, res, next) {
  var _json = res.json;
  res.json = (obj) => {
    res.json = _json;
    obj = replaceIds(obj, (v) => hashids.encode(v));
    return res.json(obj);
  };
  next();
}
function decodeMiddleware(req, res, next) {
  try {
    req.query = replaceIds(req.query, (v) => hashids.decode(v)[0]);
    req.body = replaceIds(req.body, (v) => hashids.decode(v)[0]);
  } catch (e) {
    console.error(`Could not decode id:`, e);
    return res.sendStatus(404);
  }
  next();
}

const app = express();
app.use(express.json());

// we're using the middleware globaly here
app.use(encodeMiddleware);
app.use(decodeMiddleware);

// sample endpoints to demonstrate encoding, decoding
app.get("/get-id", (req, res) => {
  res.json({ id: 5, name: "John" });
});
app.post("/send-id", (req, res) => {
  console.log(req.body);
  res.sendStatus(200);
});

app.listen(3000);


인코딩 및 디코딩



GET 끝점을 호출하면 idname를 사용하여 일부 JSON 데이터를 반환해야 합니다.

> curl GET http://localhost:3000/get-id

{"id":"OPZexb","name":"John"}%

id가 자동으로 인코딩된 것을 제외하고 우리가 한 일입니다. JSON 본문에서 _id로 끝나는 항목을 반환하면 미들웨어가 자동으로 인코딩합니다.

이제 인코딩된 id를 POST 끝점으로 전송해 보겠습니다.

> curl -X POST http://localhost:3000/send-id \
   -H 'Content-Type: application/json' \
   -d '{"id":"OPZexb"}'
...

[server log]: { id: 5 }


그리고 서버에서 { id: 5 } 를 확인해야 합니다. 이는 미들웨어가 우리가 보낸 id를 성공적으로 디코딩했음을 의미합니다. 마찬가지로 _id를 포함할 수 있는 값을 보내면 자동으로 디코딩됩니다.

마무리 메모



요청 또는 응답 본문에서 모든 id 를 찾아 필요에 따라 인코딩 또는 디코딩하는 글로벌 미들웨어를 추가할 수 있었습니다.

프로덕션에서는 이 미들웨어가 타사 서비스의 웹훅에서 실행되는 것을 방지하기 위해 필터를 추가할 수 있습니다. id , _id 구문 자체를 사용할 수 있기 때문입니다.

좋은 웹페이지 즐겨찾기