웹사이트 검색

체크섬으로 리소스 식별자를 생성하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

고유 식별자(UID) 또는 식별자는 문자열 값 또는 정수일 수 있으며 API 개발자는 종종 이를 사용하여 API의 고유 리소스를 처리합니다. 그런 다음 API 소비자는 이러한 식별자를 사용하여 리소스 모음에서 단일 리소스를 가져옵니다. 고유한 식별자가 없으면 리소스를 분리하고 필요에 따라 호출하는 것이 거의 불가능합니다. 식별자는 테이블 이름, 테이블의 필드(열) 또는 제약 조건과 같은 데이터베이스 구조 요소를 참조할 수 있으며 데이터베이스의 고유 항목에 추가로 지정할 수 있습니다. 예를 들어 호텔 예약 포털과 관련된 데이터베이스에서 Hotel(id)는 고유한 호텔을 나타내는 식별자를 가리킬 수 있습니다. 호텔(id=1234, name=\Hyatt\)을 사용하면 ID 1234 또는 이름 \하얏트\로 특정 호텔을 식별할 수 있습니다. .

API 디자인 패턴에서 John J. Geewax는 좋은 식별자에 대한 7가지 기본 특성을 식별합니다. 고유 ID를 생성할 때 다음 특성을 고려해야 합니다.

  • 사용하기 쉬움: 슬래시(/)와 같은 예약 문자는 URL에서 특정 의미를 수행하므로 식별자에 사용하지 않아야 합니다.
  • 고유: 식별자는 API의 단일 리소스를 참조할 수 있어야 합니다.
  • 신속한 생성: ID 생성 프로세스는 확장 시 일관성을 위해 예측 가능한 방식으로 수행되어야 합니다.
  • 예측 불가능: 식별자를 예측할 수 없는 경우 취약성 관리에 보안상의 이점을 제공합니다.
  • 읽기 가능: 식별자는 사람이 읽을 수 있어야 하며 숫자 1, 소문자 L, 대문자 I 또는 파이프 문자(|)는 누군가 ID를 수동으로 확인해야 하는 경우 혼동을 일으킬 수 있습니다.
  • 확인 가능: 체크섬 문자를 사용하여 무결성 검사 중에 ID를 확인할 수 있습니다.
  • 영구적: 일단 할당된 식별자는 변경되지 않아야 합니다.

참고: 식별자를 변경하면 예기치 않은 혼동이 발생할 수 있습니다. Hotel(id=1234, name=\Hyatt\)을 지정하고 나중에 Hotel(id=5678, name=\Hyatt\)로 변경되는 식별자가 있는 경우 , 이전 ID를 재사용할 수 있습니다. 이전 식별자를 사용할 수 있고 새 호텔이 Hotel(id=1234, name=\Grand Villa\)로 생성된 경우 이 새 호텔은 원래 식별자(1234 ). 그런 다음 호텔 1234를 요청하면 예상과 다른 결과를 받을 수 있습니다.

이 자습서에서는 Node.JS를 사용하여 이러한 특성과 관련 체크섬을 충족하는 고유한 사용자 지정 리소스 식별자를 생성합니다. 디지털 객체에 대한 해시 함수입니다. 이 자습서의 체크섬은 리소스에 해당하는 바이트 크기에 대한 인코딩(또는 해싱) 알고리즘 프로세스에 의해 파생된 단일 영숫자 문자입니다.

전제 조건

이 튜토리얼을 시작하기 전에 다음이 필요합니다.

  • 컴퓨터에 Node.js가 설치되어 있습니다. Node.js 설치 방법에 따라 설정할 수 있습니다. 이 튜토리얼은 Node.JS 버전 16.16.0에서 테스트되었습니다.
  • Node.js에 익숙함. Node.js 코딩 방법 시리즈에서 자세히 알아보세요.
  • API에 대한 지식. API 작업에 대한 포괄적인 자습서는 Python3에서 웹 API를 사용하는 방법을 검토할 수 있습니다. 이 문서는 Python용으로 작성되었지만 API 작업의 핵심 개념을 이해하는 데 도움이 됩니다.
  • Sublime Text와 같은 JavaScript 구문 강조를 지원하는 텍스트 편집기입니다. 이 자습서에서는 명령줄 편집기 nano를 사용합니다.

1단계 - 인코딩된 ID 생성

이 단계에서는 임의의 바이트에서 고유한 영숫자 문자열로 식별자를 생성하는 함수를 작성합니다. 식별자는 base32 인코딩을 사용하여 인코딩되지만 자습서의 후반부까지 연결된 체크섬이 없습니다. 인코딩 프로세스는 선택한 바이트 수에 따라 지정된 길이의 고유 식별자를 생성하여 좋은 ID의 일부 특성을 통합하는 ID를 만듭니다.

이 프로젝트의 새 폴더를 만든 다음 해당 폴더로 이동합니다.

  1. mkdir checksum
  2. cd checksum

이 튜토리얼에서는 프로젝트 폴더를 checksum이라고 합니다.

선호하는 편집기를 사용하여 프로젝트 폴더에서 package.json 파일을 만들고 엽니다.

  1. nano package.json

그런 다음 다음 코드 줄을 추가합니다.

{
  "name": "checksum",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module"
}

이 파일에서 프로젝트 이름을 checksum로 정의하고 코드 버전 \1.0.0\ 를 고려합니다. . 기본 JavaScript 파일을 index.js로 정의합니다. package.json 파일에 \type\: \module\이 있는 경우 소스 코드는 JavaScript에서 JSON으로 작업하는 방법을 사용해야 합니다.

파일을 저장하고 닫습니다.

몇 가지 Node.js 모듈을 사용하여 base32-decode라는 ID를 생성합니다. crypto 모듈은 Node.JS와 함께 제공되지만 이 튜토리얼의 뒷부분에서 사용하려면 base32-encodebase32-decode를 설치해야 합니다. . 인코딩은 효율적인 전송 또는 저장을 위해 일련의 문자(문자, 숫자, 구두점 및 특정 기호)를 특수 형식으로 변환하는 것입니다. 디코딩은 반대 프로세스입니다. 즉, 인코딩된 형식을 원래 문자 시퀀스로 다시 변환하는 것입니다. Base32 인코딩은 32자 세트를 사용하므로 숫자를 표현하기 위한 텍스트 32기호 표기법이 됩니다.

터미널 세션에서 다음 명령을 사용하여 프로젝트 폴더에 이러한 모듈 패키지를 설치합니다.

  1. npm i base32-encode base32-decode

이러한 모듈이 추가되었음을 나타내는 출력을 받게 됩니다.

Output
added 3 packages, and audited 5 packages in 2s found 0 vulnerabilities

설치 중에 문제가 발생하면 지원을 위해 npm 및 package.json과 함께 Node.js 모듈을 사용하는 방법을 참조할 수 있습니다.

여전히 프로젝트 폴더에서 index.js라는 새 파일을 만듭니다.

  1. nano index.js

index.js 파일에 다음 JavaScript 코드 줄을 추가합니다.

import crypto from 'crypto';  
import base32Encode from 'base32-encode';
import base32Decode from 'base32-decode';
 
function generate_Id(byte_size) {
    const bytes = crypto.randomBytes(byte_size);
    return base32Encode(bytes, 'Crockford');
}

console.log('ID for byte size = 1:',generate_Id(1), '\n');
console.log('ID for byte size = 12:',generate_Id(12), '\n');
console.log('ID for byte size = 123:',generate_Id(123), '\n');

import 명령은 필수 모듈을 로드합니다. 숫자에서 바이트를 생성하려면 generate_Id 함수를 정의하여 바이트의 바이트 크기를 가져온 다음 randomBytes 함수를 사용하여 이 크기의 임의 바이트를 생성합니다. >crypto 모듈. 그런 다음 generate_Id 함수는 base32 인코딩의 Crockford 구현을 사용하여 이러한 바이트를 인코딩합니다.

설명을 위해 몇 개의 ID가 생성된 다음 콘솔에 기록됩니다. base32-decode 모듈은 다음 단계에서 리소스 ID를 디코딩하는 데 사용됩니다.

index.js 파일을 저장한 다음 다음 명령을 사용하여 터미널 세션에서 코드를 실행합니다.

node index.js

다음과 유사한 출력 응답을 받게 됩니다.

Output
ID for byte size = 1: Y8 ID for byte size = 12: JTGSEMQH2YZFD3H35HJ0 ID for byte size = 123: QW2E2KJKM8QZ7174DDB1Q3JMEKV7328EE8T79V1KG0TEAE67DEGG1XS4AR57FPCYTS24J0ZRR3E6TKM28AM8FYZ2AZTZ55C9VVQTABE0R7QRH7QBY7V3GBYBNN5D9JK0QMD9NXSWZN95S0772DHN43Q003G0QNTPA2J3AFA3P7Q167C1VNR92Z85PCDXCMEY0M7WA

생성된 바이트의 임의성으로 인해 ID 값이 다를 수 있습니다. 생성된 ID는 선택한 바이트 크기에 따라 길이가 더 짧거나 길 수 있습니다.

index.js로 돌아가 JavaScript 주석 달기 기능을 사용하여 콘솔 출력을 주석 처리합니다(줄 앞에 이중 슬래시 // 추가).

...
//console.log('ID for byte size = 1:',generate_Id(1), '\n'); 
//console.log('ID for byte size = 12:',generate_Id(12), '\n');
//console.log('ID for byte size = 123:',generate_Id(123), '\n');

이 줄은 인코딩이 연결된 바이트에 따라 다른 식별자를 출력하는 방법을 보여줍니다. 이러한 줄은 다음 섹션에서 사용되지 않으므로 이 코드 블록에 설명된 대로 주석 처리하거나 완전히 삭제할 수 있습니다.

이 단계에서는 임의의 바이트를 인코딩하여 인코딩된 ID를 만들었습니다. 다음 단계에서는 인코딩된 바이트와 체크섬을 결합하여 고유한 식별자를 만듭니다.

2단계 - 리소스 식별자 생성

이제 체크섬 문자로 ID를 생성합니다. 체크섬 문자 생성은 2단계 프로세스입니다. 설명을 위해 복합 함수를 생성하는 각 함수는 다음 하위 섹션에서 별도로 빌드됩니다. 먼저 모듈로 연산을 실행하는 함수를 작성합니다. 그런 다음 리소스 ID에 대한 체크섬을 생성하는 방법인 체크섬 문자에 결과를 매핑하는 다른 함수를 작성합니다. 마지막으로 리소스 식별자가 정확한지 확인하기 위해 식별자와 체크섬을 확인합니다.

모듈로 작업 실행

이 섹션에서는 숫자 ID에 해당하는 바이트를 0-36(제한 포함, 0에서 36 사이의 숫자를 의미하는 < 코드>0 및 <코드>36). 숫자 ID에 해당하는 바이트는 BigInteger(BigInt) 값의 결과로 정수로 변환됩니다.

이 절차를 구현하려면 index.js 파일 맨 아래에 다음 코드 줄을 추가합니다.

...

function calculate_checksum(bytes) {
    const intValue = BigInt(`0x${bytes.toString('hex')}`);
    return Number(intValue % BigInt(37));
}

함수 calculate_checksum은 파일에서 이전에 정의된 바이트와 함께 작동합니다. 이 함수는 바이트를 BigInteger BigInt 값으로 변환되는 16진수 값으로 변환합니다. BigInt 데이터 유형은 Javascript의 기본 데이터 유형 숫자가 나타내는 것보다 큰 숫자를 나타냅니다. 예를 들어, 정수 37은 상대적으로 작지만 모듈로 연산을 위해 BigInt로 변환됩니다.

이 변환을 수행하려면 먼저 BigInt 변환 방법으로 intValue 변수를 설정하고 toString 방법을 사용하여 bytes를 설정합니다.에서 hex로. 그런 다음 Number 생성자로 숫자 값을 반환합니다. 여기서 % 기호로 모듈로 연산을 실행하여 intValue 사이의 나머지를 찾습니다. 및 BigInt37의 샘플 값을 사용합니다. 이 정수 값(이 예에서는 37)은 사용자 지정 빌드 영숫자 문자열에서 영숫자 문자를 선택하는 인덱스 역할을 합니다.

intValue 값이 123인 경우(바이트에 따라 다름) 모듈 작업은 123 % 37이 됩니다. 정수 값이 37인 이 작업의 결과는 12의 나머지와 3의 몫이 됩니다. 리소스 ID의 값이 154인 경우 작업 154 % 37은 나머지 6이 됩니다.

이 함수는 들어오는 바이트를 모듈로 결과에 매핑합니다. 다음으로 모듈로 결과를 체크섬 문자에 매핑하는 함수를 작성합니다.

체크섬 문자 얻기

이전 섹션에서 모듈로 결과를 얻은 후 체크섬 문자에 매핑할 수 있습니다.

이전 코드 바로 아래에 있는 index.js 파일에 다음 코드 줄을 추가합니다.

...

function get_checksum_character(checksumValue) {
    const alphabet = '0123456789ABCDEFG' +
        'HJKMNPQRSTVWXYZ*~$=U';  
    return alphabet[Math.abs(checksumValue)]; // 
}

함수 get_checksum_character의 경우 checksumValue를 매개변수로 호출합니다. 이 함수 내에서 alphabet이라는 문자열 상수를 영숫자 문자열로 정의합니다. checksumValue에 대해 설정된 값에 따라 이 함수는 alphabet 상수에서 정의된 문자열을 checksumValue의 절대값과 쌍을 이루는 값을 반환합니다. 코드>.

다음으로 체크섬 문자와 결합된 바이트 인코딩에서 ID를 생성하기 위해 이 섹션에 작성된 두 함수를 사용하는 함수를 작성합니다.

index.js 파일에 다음 코드 줄을 추가합니다.

... 

function generate_Id_with_checksum(bytes_size) {
    const bytes = crypto.randomBytes(bytes_size);
    const checksum = calculate_checksum(bytes);
    const checksumChar = get_checksum_character(checksum);
    console.log("checksum character: ", checksumChar); 
    const encoded = base32Encode(bytes, 'Crockford');
    return encoded + checksumChar;
}

const Hotel_resource_id =generate_Id_with_checksum(132)
console.log("Hotel resource id: ",Hotel_resource_id)

이 코드 섹션은 이전의 두 함수인 calculate_checksumget_checksum_character(체크섬 문자를 생성하는 데 사용됨)와 인코딩 함수를 라는 이름의 새 함수로 결합합니다. 체크섬 문자로 ID를 생성하는 generate_Id_with_checksum.

파일을 저장한 다음 별도의 터미널 세션에서 코드를 실행합니다.

  1. node index.js

다음과 유사한 출력이 표시됩니다.

Output
checksum character: B Hotel resource id: 9V99B9P55K7M4DN5XYP4VTJYJGENZKJ0F9Q6EEEZ07X49G0V14AXJS3RYXBT3J1WJZXWGM76C6H7G895TJT27AW77BHBX2D16QNQ2ZNBY9MQHWG9NJ1WWVTNRCKRBX6HC3M7BB3JG0V413VJ767JN6FT0GFS5VQJ9X7KSP1KM29B02NAGXN3FP30WA8Y63N1XJAMGDPEE1RNHRTWH6P0B

체크섬이 일치함을 나타내는 동일한 체크섬 문자가 ID 끝에 나타납니다.

이 계통도는 이 복합 기능이 작동하는 방식을 구조적으로 보여줍니다.

이 흐름도는 리소스에 대한 카운터에서 수동으로 생성한 식별자인 제품 ID가 인코딩 및 모듈로 프로세스를 통해 고유한 리소스 ID로 변환되는 방법을 보여줍니다. 다이어그램의 crypto 메서드crypto.randomBytes() 함수를 참조합니다.

체크섬 문자를 포함할 바이트 크기를 기반으로 ID를 생성했습니다. 다음 섹션에서는 identifier 함수를 구현하여 base32-decoding으로 ID의 무결성을 확인합니다.

식별자의 무결성 확인

무결성을 보장하기 위해 이제 verify_Id라는 새 함수를 사용하여 체크섬 문자(식별자의 마지막 문자)를 생성된 체크섬과 비교합니다. 체크섬 문자 비교는 원래 ID의 무결성을 확인하고 변조되지 않았는지 확인하는 필수 단계입니다.

index.js 파일에 다음 줄을 추가합니다.

...
function verify_Id(identifier) {
    const value = identifier.substring( 0, identifier.length-1);
    const checksum_char = identifier[identifier.length-1];     
    const buffer = Buffer.from( base32Decode(value, 'Crockford'));
    const calculated_checksum_char = get_checksum_character(calculate_checksum(buffer));
    console.log(calculated_checksum_char);
    const flag =calculated_checksum_char== checksum_char;
    return (flag);    
     }
console.log('\n');
console.log("computing checksum")
const flag = verify_Id(Hotel_resource_id);
if (flag) console.log("Checksums matched.");
else console.log("Checksums did not match.");

verify_Id 기능은 체크섬을 확인하여 ID의 무결성을 확인합니다. 식별자의 나머지 문자는 버퍼로 디코딩된 다음 calculate_checksumget_checksum_character가 이 버퍼에서 연속적으로 실행되어 다음에 대한 체크섬 문자를 추출합니다. 비교(calculated_checksum_char== checksum_char 사용).

이 회로도는 복합 기능이 어떻게 작동하는지 보여줍니다.

이 차트에서 슬라이싱은 체크섬 문자(checksum)에서 ID 값(value)을 분리하는 것을 의미합니다. 이전 코드 블록에서 identifier.substring( 0, identifier.length-1) 함수는 ID 값을 선택하는 반면 identifier[identifier.length-1] 리소스 ID에서 마지막 문자를 가져옵니다.

이제 index.js 파일이 다음 코드와 일치해야 합니다.

import crypto from 'crypto';  // for generating bytes from the number
import base32Encode from 'base32-encode'; // for encoding the bytes into Unique ID as string type
import base32Decode from 'base32-decode';// for decoding the ID into bytes

function generate_Id(byte_size) {
    const bytes = crypto.randomBytes(byte_size);
    return base32Encode(bytes, 'Crockford');
}

//console.log('ID for byte size = 1:',generate_Id(1), '\n');
//console.log('ID for byte size = 12:',generate_Id(12), '\n');
//console.log('ID for byte size = 123:',generate_Id(123), '\n');

function calculate_checksum(bytes) {
    const intValue = BigInt(`0x${bytes.toString('hex')}`);
    return Number(intValue % BigInt(37));
}

function get_checksum_character(checksumValue) {
    const alphabet = '0123456789ABCDEFG' +
        'HJKMNPQRSTVWXYZ*~$=U'; // custom-built string  consisting of alphanumeric character
    return alphabet[Math.abs(checksumValue)]; // picking out an alphanumeric character
}

function generate_Id_with_checksum(bytes_size) {
    const bytes = crypto.randomBytes(bytes_size);
    const checksum = calculate_checksum(bytes);
    const checksumChar = get_checksum_character(checksum);
    console.log("checksum character: ", checksumChar); 
    const encoded = base32Encode(bytes, 'Crockford');
    return encoded + checksumChar;
}

const Hotel_resource_id =generate_Id_with_checksum(132)
console.log("Hotel resource id: ",Hotel_resource_id)

function verify_Id(identifier) {
    const value = identifier.substring( 0, identifier.length-1);
    const checksum_char = identifier[identifier.length-1]; 
    //console.log(value,checksum_char);
    const buffer = Buffer.from( base32Decode(value, 'Crockford'));
    const calculated_checksum_char = get_checksum_character(calculate_checksum(buffer));
    console.log(calculated_checksum_char);
    const flag =calculated_checksum_char== checksum_char;

    return (flag);
    
     }

console.log('\n');
console.log("computing checksum")
const flag = verify_Id(Hotel_resource_id);
if (flag) console.log("Checksums matched.");
else console.log("Checksums did not match.");

이제 다음 코드를 실행할 수 있습니다.

node index.js

다음 출력을 받게 됩니다.

Output
... computing checksum AW75SY7FVC7TKT7VP5ZF0M8C67CN36YZK27BXHVFHSDXJFKH54HK2AXQFMPN89Q5YQRPGNHGAYQ5JFKVD40EKTXCET97Q0FEPX6MX1ZTNWGCA08SBRSHP8B0037ACJG6F6472FEVARCAWM6P5MRJ2F6WTRPXHYS9N1JEDZVH41D33RA5365VNFC5G5VYEFPFJJD8151B28XXDBRHAF80 H H Checksums matched.

이제 체크섬 문자를 사용하여 식별자의 무결성을 확인하는 verify_Id라는 함수가 있습니다. 다음으로, 교육용으로 리소스 ID를 변경하여 함수가 일치하지 않는 결과를 제공하여 확인에 실패할 때 발생하는 상황을 평가할 수 있습니다.

(선택 사항) 3단계 - 일치하지 않는 결과에 대한 식별자 변경

이제 체크섬이 일치하는지 확인하기 위해 식별자 값을 변경합니다. ID의 문자가 조작되면 무결성이 유지되지 않으므로 이 단계에서 변경하면 항상 일치하지 않는 체크섬이 발생합니다. 이와 같은 변경은 전송 오류 또는 악의적인 동작으로 인해 발생할 수 있습니다. 이 변경은 교육용이며 프로덕션 빌드에는 권장되지 않지만 일치하지 않는 체크섬 결과를 평가할 수 있습니다.

index.js 파일에서 강조 표시된 줄을 추가하여 Hotel_resource_id를 수정합니다.

...
const altered_Hotel_resource_id= Hotel_resource_id.replace('P','H');   
console.log("computing checksum")
const flag = verify_Id(altered_Hotel_resource_id);
if (flag) console.log("Checksum matched.");
else console.log("Checksums did not match.");

위의 코드에서 ID의 PH로 바꾸고 변수 이름을 Hotel_resource_ID에서 altered_Hotel_resource_id로 바꿉니다. . 다시 말하지만 이러한 변경 사항은 정보 제공을 위한 것이며 일치 무결성을 보장하기 위해 이 단계가 끝날 때 되돌릴 수 있습니다.

파일을 저장한 다음 리소스 ID를 변경하여 코드를 다시 실행합니다.

  1. node index.js

체크섬이 일치하지 않는다는 출력을 받게 됩니다.

Output
Checksums did not match.

이 단계에서는 체크섬이 무결성 테스트를 통과했는지 여부를 확인하는 함수를 만들었고 두 경우 모두 발생했습니다. 일치하지 않는 체크섬은 리소스 ID가 조작되었음을 나타냅니다. 알림을 통해 개발자는 애플리케이션 요구 사항에 따라 사용자 요청을 차단하거나 리소스 ID와 관련된 요청을 보고하는 등 악의적인 동작에 대해 조치를 취할 수 있습니다.

함수를 일치하는 체크섬 결과로 되돌리려면 이 단계의 시작 부분에 추가된 추가 코드를 삭제하여 코드가 2단계 끝에 있는 파일과 일치하도록 합니다.

체크섬이 포함된 고유한 사용자 지정 ID가 필요한 경우 이 자습서를 사용하여 데이터 모델 생성, API 버전 관리 등에 도움을 받을 수 있습니다.

결론

이 자습서에서는 좋은 식별자의 특성에 맞는 리소스 ID를 개발했습니다. 또한 base32-encoding을 사용하여 Node.js 환경에서 체크섬으로 고유한 리소스 ID를 만들었습니다. 마지막으로 base32-decoding으로 디코딩하여 ID의 무결성을 확인했습니다.

교차 확인을 위해 최종 파일을 GitHub 소개 및 오픈 소스 프로젝트 시리즈의 파일과 비교할 수 있습니다.

이제 체크섬의 기본 사항을 이해했으므로 MD5와 같은 다른 인코딩 알고리즘을 실험해 볼 수 있습니다.