💡 솔트추가에 대해
- 해시 함수에서 비슷한 한글을 입력하니 비슷한 해시 값을 생성하게 되는 문제가 발생하였습니다. 따라서 비슷한 이름을 입력 받아도 다른 해시 값을 생성하도록 하는 방법을 찾아야 하던 중 "솔트"라는 것을 알게 되었습니다.
1. 해시 함수의 "솔트" 추가
해시 계산에 "솔트"(salt)라는 임의의 값을 추가하여, 비슷한 이름이라도 다른 해시 값을 생성하도록 할 수 있습니다. 솔트는 해시 함수의 입력에 추가되는 고정된 문자열이며, 이로 인해 같은 입력 값에 대해서도 다른 출력이 생성됩니다.
const hashName = (name) => {
let hash = 0;
const salt = "your_salt_here"; // 솔트 추가
const combined = name + salt; // 이름과 솔트를 결합
for (let i = 0; i < combined.length; i++) {
const char = combined.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash);
};
솔트 추가했을때 해시값이 달라지는 이유?
솔트(salt)을 추가하는 주된 이유는 해시 과정에 더 많은 무작위성을 도입하여 결과의 고유성을 증가시키고, 레인보우 테이블(rainbow table) 공격과 같은 해킹 시도를 방어하기 위함입니다. 솔트를 사용하는 것은 주로 암호 저장과 같은 보안 관련 컨텍스트에서 중요한 역할을 합니다. 해시 함수는 동일한 입력에 대해 항상 동일한 출력을 생성하는 결정론적 특성을 가지고 있기 때문에, 이러한 특성을 악용하는 여러 공격 방법이 존재합니다.
솔트가 해시값에 미치는 영향
- 고유성 증가: 솔트를 추가함으로써, 같은 데이터에 대해서도 서로 다른 솔트를 사용하면 결과 해시값이 달라집니다. 이는 해시 충돌의 확률을 낮추고, 각 입력값의 해시가 고유하게 유지되도록 도와줍니다.
- 레인보우 테이블 공격 방어: 레인보우 테이블은 사전에 계산된 해시값의 대대적인 데이터베이스입니다. 공격자는 이 테이블을 사용하여 해시값을 원래의 입력값으로 역산할 수 있습니다. 하지만, 솔트를 사용하면 레인보우 테이블을 무력화할 수 있습니다. 솔트가 각 입력값마다 다르게 적용되기 때문에, 공격자가 각 가능한 솔트의 조합에 대해 레인보우 테이블을 미리 계산해 두는 것은 현실적으로 불가능합니다.
- 사전 공격(dictionary attack) 방어: 사전 공격은 공격자가 가능한 모든 패스워드 조합을 시도하여 해시값과 일치하는 값을 찾는 공격 방식입니다. 솔트를 사용하면, 심지어 단순하고 자주 사용되는 패스워드라 할지라도, 해시 과정에서 솔트가 추가되어 결과적으로 해시값이 유니크해지므로 이러한 공격을 효과적으로 방어할 수 있습니다.
결론
솔트를 추가하는 것은 해시 과정에서 데이터의 안전성을 강화하는 중요한 방법입니다. 해시값의 고유성을 증가시키고, 해킹 시도로부터 데이터를 보호하는 데 도움을 줍니다. 특히, 암호와 같은 민감한 정보를 저장할 때 솔트를 사용하는 것은 현대 보안 관행에서 필수적인 부분입니다. 솔트를 통해 해시 과정에 무작위성을 추가함으로써, 해시 함수의 결정론적 특성을 보완하고 보안을 강화할 수 있습니다.
솔트(salt)를 추가하여 해시 함수가 동작하는 과정
솔트(salt)을 추가하여 해시 함수가 동작하는 과정을 자세히 설명하겠습니다. 이 과정은 데이터(예: 비밀번호)를 보호하는 데 중요한 역할을 합니다. 솔트를 사용하는 주된 목적은 해시된 값에 무작위성을 추가하여, 원본 데이터의 보안을 강화하는 것입니다. 아래는 솔트를 사용한 해시 과정의 단계별 설명입니다:
1. 솔트의 생성
- 솔트는 무작위로 생성된 데이터 조각입니다. 보통 충분한 길이의 문자열로, 각 사용자나 데이터 항목마다 고유하게 생성됩니다. 이 과정은 자동화되어 있으며, 솔트는 각 데이터 항목(예: 사용자 비밀번호)에 대해 유니크해야 합니다.
2. 데이터와 솔트의 결합
- 사용자의 입력 데이터(예: 비밀번호)에 솔트를 추가합니다. 이는 일반적으로 데이터와 솔트를 단순히 연결(concatenation)하는 방식으로 이루어집니다. 예를 들어, 사용자의 비밀번호가 "password123"이고 생성된 솔트가 "s4lT"라면, 결합된 문자열은 "password123s4lT"가 됩니다.
3. 해시 함수 적용
- 결합된 데이터("password123s4lT")에 해시 함수를 적용합니다. 해시 함수는 SHA-256, SHA-3, bcrypt 등 다양한 알고리즘 중 하나를 사용할 수 있습니다. 해시 함수는 결합된 데이터를 고정된 길이의 해시값으로 변환합니다. 이 과정은 일방향 함수로, 결과 해시값에서 원본 데이터를 추론하는 것은 계산적으로 불가능합니다.
4. 해시값과 솔트의 저장
- 생성된 해시값과 솔트 모두 데이터베이스에 저장됩니다. 솔트는 원본 데이터를 추론하는 데 직접 사용되지 않지만, 데이터 검증 과정에서 입력 데이터에 다시 적용되어야 하므로, 함께 저장되어야 합니다.
5. 데이터 검증
- 사용자가 나중에 시스템에 로그인하려고 할 때, 입력한 데이터(비밀번호)를 검증해야 합니다. 이때 저장된 솔트를 해당 사용자의 입력 데이터에 추가하고, 같은 해시 함수를 적용합니다. 그런 다음, 이 과정에서 생성된 해시값을 데이터베이스에 저장된 해시값과 비교합니다. 만약 두 해시값이 일치한다면, 입력 데이터가 원본 데이터와 동일함이 검증되고, 사용자는 시스템에 접근할 수 있습니다.
솔트 사용의 중요성
솔트를 사용함으로써, 동일한 데이터에 대해서도 다른 솔트가 적용될 때마다 다른 해시값이 생성됩니다. 이는 해시 충돌의 위험을 줄이고, 레인보우 테이블 공격과 같은 공격 기법을 효과적으로 방어할 수 있도록 합니다. 또한, 솔트는 데이터베이스 내의 해시된 데이터 간에도 무작위성을 증가시켜, 공격자가 패턴을 식별하는 것을 어렵게 만듭니다.
이러한 과정을 통해, 솔트를 추가하는 것은 데이터의 보안을 강화하고, 암호화된 정보의 안전성을 보장하는 데 필수적인 방법입니다.
위의 예제 코드에서 사용된 함수 설명
💡 charCodeAt()역할?
charCodeAt() 메서드는 JavaScript에서 문자열 내 특정 인덱스에 위치하는 문자의 유니코드 값을 반환합니다. 유니코드는 전 세계의 모든 문자를 일관되게 표현하고 조작하기 위한 산업 표준입니다. 이 메서드를 사용하면, 주어진 문자의 유니코드 숫자 값을 얻을 수 있어, 문자 데이터를 숫자로 변환하여 다루는 다양한 알고리즘과 처리에서 유용하게 사용됩니다.
for 루프는 combined 문자열의 처음부터 끝까지 각 문자에 대해 반복하며, **i**는 0에서 시작해 **combined.length - 1**까지 증가합니다. 따라서, 루프 내의 모든 문자에 대해 다음을 수행합니다:
- **const char = combined.charCodeAt(i);**에서 현재 인덱스 **i**에 있는 문자의 유니코드 값을 char 변수에 할당합니다.
- 이 char 값은 해시 계산에 사용되며, **hash = (hash << 5) - hash + char;**를 통해 hash 값에 반영됩니다. 여기서, 각 문자의 유니코드 값이 해시 계산 과정에 차례대로 포함됩니다.
- **hash = hash & hash;**는 32비트 정수로 해시 값을 유지하기 위한 연산입니다. 이 부분은 실제로는 해시 값에 변화를 주지 않으며, 의도된 것이 아닐 수 있습니다. **hash & hash**는 hash 값 자체와 동일한 결과를 반환합니다. 여기서 의도했던 것은 아마도 해시 값을 32비트 정수 범위 내로 제한하려는 것일 텐데, hash |= 0; 또는 비슷한 연산이 이 목적에 더 적합할 수 있습니다.
💡 |=의 사용목적
|= 연산자는 비트 OR 할당 연산자입니다. 이 연산자는 변수의 값을 변경하기 위해 비트 OR 연산(|)의 결과를 해당 변수에 할당하는 데 사용됩니다. 비트 OR 연산은 두 피연산자의 각 비트를 비교하여, 어느 한 쪽이라도 1이면 결과 비트를 1로 설정합니다.
|= 연산자의 동작 방식
a |= b;는 a = a | b;와 동일한 작업을 수행합니다. 즉, a와 b의 각 비트를 비교하여 둘 중 하나라도 1이면 결과 비트를 1로 설정한 후, 그 결과를 다시 a에 할당합니다.
예시
let a = 3; // 이진수로 0011
let b = 6; // 이진수로 0110
a |= b;
이 경우, a |= b; 연산 후의 a의 값은 다음과 같이 계산됩니다:
- 0011 (3의 이진 표현)
- 0110 (6의 이진 표현)
- 0111 (비트 OR 연산의 결과, 이진수로 7)
따라서, a의 새로운 값은 7이 됩니다.
|= 0 사용 목적
hash |= 0; 연산은 hash 변수의 값을 32비트 정수로 강제로 변환하는 데 사용될 수 있습니다. JavaScript에서 정수는 기본적으로 64비트 부동 소수점 형식으로 저장되지만, 비트 연산을 수행할 때는 32비트 정수로 처리됩니다. 따라서, 이 연산은 hash의 값을 32비트 정수의 범위로 제한하려는 목적으로 사용될 수 있으며, 이는 특히 큰 수를 다룰 때 유용할 수 있습니다.
이 방식은 변수의 값을 변경하지 않으면서 변수의 타입을 32비트 정수로 강제할 때 사용됩니다. 예를 들어, hash가 이미 32비트 정수라면, hash |= 0; 연산 후에도 hash의 값은 변하지 않습니다. 하지만, 이 연산은 hash 값을 명시적으로 32비트 정수의 형태로 유지하거나 변환하는 데 도움을 줍니다.
'JS' 카테고리의 다른 글
APIkey 코드에서 숨기기 (0) | 2024.02.19 |
---|---|
영화 세부 정보를 반환하는 내부 API 작성 (APIkey 숨기기) (0) | 2024.02.18 |