> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 해시(Hashing)

> Laravel의 Hash 파사드를 사용한 안전한 비밀번호 해시화·검증 방법을 설명합니다. bcrypt와 Argon2 알고리즘의 설정과 사용법을 소개합니다.

## 해시란

해시(Hashing)란 비밀번호 같은 평문 데이터를 단방향 변환으로 고정 길이의 문자열로 변환하는 처리입니다.
같은 입력에서는 항상 같은 해시가 생성되지만, 해시에서 원래 평문을 복원할 수는 없습니다.

Laravel의 `Hash` 파사드는 안전한 비밀번호 저장을 위해 **bcrypt**와 **Argon2** 해시 알고리즘을 지원합니다.

### 알고리즘 비교

| 알고리즘         | 특징                                    | 권장 용도                     |
| ------------ | ------------------------------------- | ------------------------- |
| **bcrypt**   | 워크 팩터(rounds)로 계산 비용을 조정할 수 있다. 기본값   | 일반적인 웹 애플리케이션             |
| **argon2i**  | 메모리·시간·스레드 수를 조정할 수 있다. 사이드 채널 공격에 강함 | 높은 보안이 요구되는 상황            |
| **argon2id** | argon2i와 argon2d의 하이브리드. PHC 권장       | 신규 프로젝트에서 Argon2를 사용하는 경우 |

<Info>
  bcrypt의 "워크 팩터"는 해시 생성에 걸리는 시간을 제어합니다. 해시가 느릴수록 브루트포스 공격에 대한 내성이 높아집니다. 하드웨어가 빨라짐에 따라 워크 팩터를 올리면 보안을 유지할 수 있습니다.
</Info>

### 해시화와 검증의 플로우

```mermaid theme={null}
flowchart LR
    A[평문 비밀번호] -->|Hash::make| B["해시 값<br>DB에 저장"]
    C[로그인 시 입력] -->|Hash::check| D{일치?}
    B --> D
    D -->|true| E[인증 성공]
    D -->|false| F[인증 실패]
```

***

## 설정

기본적으로 Laravel은 `bcrypt` 드라이버를 사용합니다. `HASH_DRIVER` 환경 변수로 변경할 수 있습니다.

```ini theme={null}
# .env
HASH_DRIVER=bcrypt  # bcrypt / argon / argon2id
```

해시 설정을 커스터마이징하는 경우, `config:publish`로 설정 파일을 게시합니다.

```shell theme={null}
php artisan config:publish hashing
```

게시 후 `config/hashing.php`에서 기본 워크 팩터 등을 변경할 수 있습니다.

***

## 기본 사용법

### 비밀번호 해시화하기

`Hash::make()`에 비밀번호의 평문을 전달하면 해시 값이 반환됩니다. 이 해시 값을 데이터베이스에 저장합니다.

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class PasswordController extends Controller
{
    public function update(Request $request): RedirectResponse
    {
        $request->validate([
            'password' => ['required', 'min:8', 'confirmed'],
        ]);

        $request->user()->fill([
            'password' => Hash::make($request->password),
        ])->save();

        return redirect('/profile');
    }
}
```

<Warning>
  해시 값은 데이터베이스에 저장하지만, 평문 비밀번호는 절대 저장하지 마세요.
</Warning>

### bcrypt의 워크 팩터 조정

`rounds` 옵션으로 해시 생성의 계산 비용을 조정할 수 있습니다. 값이 클수록 안전하지만 처리 시간도 늘어납니다.
기본값(12)은 대부분의 애플리케이션에서 적절합니다.

```php theme={null}
$hashed = Hash::make('plain-text-password', [
    'rounds' => 14,
]);
```

### Argon2의 워크 팩터 조정

Argon2를 사용하는 경우, `memory`·`time`·`threads` 옵션으로 계산 비용을 조정할 수 있습니다.

```php theme={null}
$hashed = Hash::make('plain-text-password', [
    'memory'  => 65536, // KiB 단위
    'time'    => 4,
    'threads' => 2,
]);
```

| 옵션        | 설명              | 기본값   |
| --------- | --------------- | ----- |
| `memory`  | 사용하는 메모리 양(KiB) | 65536 |
| `time`    | 반복 횟수           | 4     |
| `threads` | 사용하는 스레드 수      | 1     |

<Tip>
  Argon2 옵션의 자세한 내용은 [PHP 공식 문서](https://secure.php.net/manual/ja/function.password-hash.php)를 참조해 주세요.
</Tip>

***

## 비밀번호 검증

`Hash::check()`를 사용하면 평문 비밀번호가 저장된 해시와 일치하는지 확인할 수 있습니다.
로그인 처리에서 사용하는 전형적인 예입니다.

```php theme={null}
use Illuminate\Support\Facades\Hash;

if (Hash::check($request->password, $user->password)) {
    // 비밀번호가 일치했다
} else {
    // 비밀번호가 일치하지 않는다
}
```

`Auth::attempt()`를 사용하는 경우에는 이 처리가 자동으로 이루어지므로 직접 호출할 필요는 없습니다.
`Hash::check()`가 유용한 경우는 현재 비밀번호 확인을 수동으로 수행할 때입니다.

```php theme={null}
// 비밀번호 변경 폼에서 현재 비밀번호를 확인하는 예
if (! Hash::check($request->current_password, $request->user()->password)) {
    return back()->withErrors(['current_password' => '현재 비밀번호가 올바르지 않습니다.']);
}
```

***

## 재해시 판정

`Hash::needsRehash()`를 사용하면, 해시 생성 시의 워크 팩터가 현재 설정과 다른지 확인할 수 있습니다.
워크 팩터를 변경했을 때, 기존 해시를 새 설정으로 갱신하기 위해 사용합니다.

```php theme={null}
use Illuminate\Support\Facades\Hash;

if (Hash::needsRehash($user->password)) {
    $user->update([
        'password' => Hash::make($plainTextPassword),
    ]);
}
```

로그인 성공 시에 재해시를 수행하는 예시입니다.

```mermaid theme={null}
flowchart TD
    A[로그인 성공] --> B{needsRehash?}
    B -->|true| C[새 워크 팩터로<br>재해시하여 저장]
    B -->|false| D[그대로 계속]
    C --> D
```

```php theme={null}
// 로그인 처리 안에서 재해시를 수행한다
if (Auth::attempt($credentials)) {
    if (Hash::needsRehash(Auth::user()->password)) {
        Auth::user()->update([
            'password' => Hash::make($credentials['password']),
        ]);
    }

    return redirect()->intended('/dashboard');
}
```

<Tip>
  하드웨어의 성능 향상에 맞춰 워크 팩터를 정기적으로 재검토해 보안을 유지합시다. `needsRehash()`를 활용하면 사용자의 로그인 시점이라는 자연스러운 타이밍에 비밀번호를 갱신할 수 있습니다.
</Tip>

***

## 해시 알고리즘 검증

기본적으로 `Hash::check()`는 해시가 현재 설정된 알고리즘으로 생성된 것인지를 검증합니다.
알고리즘이 다른 경우에는 `RuntimeException`이 던져집니다.

이를 통해 해시 알고리즘 변조에 의한 공격을 방지할 수 있습니다.

알고리즘을 이전하는 중 등, 여러 알고리즘을 동시에 지원해야 하는 경우에는 `HASH_VERIFY`를 `false`로 설정해 이 검증을 무효화할 수 있습니다.

```ini theme={null}
# .env
HASH_VERIFY=false
```

<Warning>
  `HASH_VERIFY=false`로 하면 알고리즘 검증이 무효화됩니다. 이행 기간 동안에만 사용하고, 이행 완료 후에는 `true`(기본값)로 되돌려 주세요.
</Warning>

***

## 정리

| 원하는 것          | 방법                           |
| -------------- | ---------------------------- |
| 비밀번호를 해시화한다    | `Hash::make($password)`      |
| 해시를 검증한다       | `Hash::check($plain, $hash)` |
| 재해시가 필요한지 확인한다 | `Hash::needsRehash($hash)`   |
| 해시 드라이버를 변경한다  | `HASH_DRIVER` 환경 변수          |
| 알고리즘 검증을 무효화한다 | `HASH_VERIFY=false`          |

## 다음 단계

<Card title="인증 입문" icon="lock" href="/ko/authentication">
  `Hash::check()`를 사용한 로그인 처리 구현 등 인증의 전체 그림을 배웁니다.
</Card>


## Related topics

- [Laravel 10에서 11로의 업그레이드](/ko/blog/upgrade-10-to-11.md)
- [TextBuilder](/ko/packages/laravel-bluesky/text-builder.md)
- [암호화(Encryption)](/ko/encryption.md)
- [GitHub Actions의 핀 고정과 보안](/ko/advanced/github-actions-pinning.md)
- [Core — AT Protocol 코어 조작](/ko/packages/laravel-bluesky/core.md)
