Add login gate
Protect UI with PHP session login and secure APIs. - Add login/logout pages and session auth helper - Serve protected content from index.php - Redirect index.html to index.php to prevent bypass - Require auth for image list/upload APIs Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
// Returns a JSON list of images currently present in /img
|
||||
require_once __DIR__ . '/../auth/auth.php';
|
||||
dreamgirl_require_login_json();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$imgDir = realpath(__DIR__ . '/../img');
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
// Upload handler: saves an uploaded image into /img and returns JSON.
|
||||
require_once __DIR__ . '/../auth/auth.php';
|
||||
dreamgirl_require_login_json();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Limit: 10MB
|
||||
|
||||
58
auth/auth.php
Normal file
58
auth/auth.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Session-based auth gate (no DB).
|
||||
* Required credentials:
|
||||
* - username: admin
|
||||
* - password: admin5004!
|
||||
*
|
||||
* NOTE: Password is stored as SHA-256 hash here (not plaintext).
|
||||
*/
|
||||
|
||||
function dreamgirl_session_start(): void {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) return;
|
||||
|
||||
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||
|
||||
// PHP 7.3+ supports samesite via array; older versions may ignore unknown keys.
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
'secure' => $secure,
|
||||
]);
|
||||
|
||||
session_start();
|
||||
}
|
||||
|
||||
function dreamgirl_is_logged_in(): bool {
|
||||
dreamgirl_session_start();
|
||||
return isset($_SESSION['dreamgirl_user']) && $_SESSION['dreamgirl_user'] === 'admin';
|
||||
}
|
||||
|
||||
function dreamgirl_check_credentials(string $username, string $password): bool {
|
||||
if ($username !== 'admin') return false;
|
||||
|
||||
// sha256("admin5004!")
|
||||
$expectedSha256 = 'adcda104b73b73f8cddf5c8047a6bc0e5e1388265ed4bf0f31f704c13cbc11b7';
|
||||
$gotSha256 = hash('sha256', $password);
|
||||
|
||||
return hash_equals($expectedSha256, $gotSha256);
|
||||
}
|
||||
|
||||
function dreamgirl_require_login_page(): void {
|
||||
if (dreamgirl_is_logged_in()) return;
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
function dreamgirl_require_login_json(): void {
|
||||
if (dreamgirl_is_logged_in()) return;
|
||||
http_response_code(401);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode(['ok' => false, 'error' => 'Unauthorized'], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 60 KiB |
101
index.html
101
index.html
@@ -1,92 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="css/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="css/galleriffic-2.css" type="text/css" />
|
||||
|
||||
<!--<script src="http://martn.ncue.net/common/js/jquery.min.js?20150610101404"></script>-->
|
||||
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.galleriffic.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.opacityrollover.js"></script>
|
||||
<script type="text/javascript" src="js/bestpic.js"></script>
|
||||
<script type="text/javascript" src="js/image_manifest.js"></script>
|
||||
<script type="text/javascript" src="js/uploader.js"></script>
|
||||
<script type="text/javascript" src="js/lightbox.js"></script>
|
||||
|
||||
<!-- We only want the thunbnails to display when javascript is disabled -->
|
||||
<script type="text/javascript">
|
||||
document.write('<style>.noscript { display: none; }</style>');
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<audio id="betoven" controls autoplay loop autostart="0" src="music/betoven.mp3">
|
||||
HTML5 Audio 를 지원하지 않는 브라우저 입니다
|
||||
</audio> <a id="toggle" href="javascript:toggle()">stop</a>
|
||||
|
||||
<div id="page">
|
||||
<div id="container">
|
||||
<h1><a href="https://ncue.net" target="_blank" rel="noopener noreferrer">NCue</a></h1>
|
||||
<!-- <h2>Thumbnail rollover effects and slideshow crossfades</h2> -->
|
||||
|
||||
<!-- Start Advanced Gallery Html Containers -->
|
||||
<div class="gallery-wrap">
|
||||
<div id="gallery" class="content">
|
||||
<div id="controls" class="controls"></div>
|
||||
<div class="slideshow-container">
|
||||
<div id="loading" class="loader"></div>
|
||||
<div id="slideshow" class="slideshow"></div>
|
||||
</div>
|
||||
<div id="caption" class="caption-container"></div>
|
||||
</div>
|
||||
<div id="thumbs" class="navigation">
|
||||
<div id="thumbs-list">
|
||||
<ul class="thumbs noscript">
|
||||
<li>
|
||||
<a class="thumb" name="leaf" href="http://dreamgirl.ncue.net/img/0.jpg" title="Title #0">
|
||||
<!-- <img src="http://dreamgirl.ncue.net/img/0.jpg" alt="Title #0" width='75' height='75'/> -->
|
||||
</a>
|
||||
<div class="caption"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="upload-panel" class="upload-panel">
|
||||
<div class="upload-title">이미지 추가</div>
|
||||
<div id="dropzone" class="dropzone" tabindex="0">
|
||||
<div class="dropzone-text">
|
||||
드래그&드랍 또는 파일 선택
|
||||
</div>
|
||||
<input id="file-input" class="file-input" type="file" accept="image/*" />
|
||||
</div>
|
||||
<div id="upload-preview" class="upload-preview" style="display:none;">
|
||||
<img id="preview-img" alt="preview" />
|
||||
<div id="preview-meta" class="preview-meta"></div>
|
||||
</div>
|
||||
<div class="upload-actions">
|
||||
<button id="upload-btn" type="button" disabled>업로드</button>
|
||||
<span id="upload-status" class="upload-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">© 2009 Trent Foley</div>
|
||||
<div id="lightbox" class="lightbox" style="display:none;" aria-hidden="true">
|
||||
<div class="lightbox-backdrop"></div>
|
||||
<button id="lightbox-close" class="lightbox-close" type="button" aria-label="Close">×</button>
|
||||
<div class="lightbox-content" role="dialog" aria-modal="true">
|
||||
<img id="lightbox-img" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// Build thumbs + init gallery after DOM is ready (and after bestpic.js runs).
|
||||
jQuery(function() {
|
||||
if (typeof onLoad === 'function') onLoad();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta http-equiv="refresh" content="0; url=/index.php" />
|
||||
<title>Redirecting...</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.location.replace('/index.php');
|
||||
</script>
|
||||
<a href="/index.php">Continue</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
93
index.php
Normal file
93
index.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/auth/auth.php';
|
||||
dreamgirl_require_login_page();
|
||||
?><!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="css/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="css/galleriffic-2.css" type="text/css" />
|
||||
|
||||
<!--<script src="http://martn.ncue.net/common/js/jquery.min.js?20150610101404"></script>-->
|
||||
<script type="text/javascript" src="js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.galleriffic.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.opacityrollover.js"></script>
|
||||
<script type="text/javascript" src="js/bestpic.js"></script>
|
||||
<script type="text/javascript" src="js/image_manifest.js"></script>
|
||||
<script type="text/javascript" src="js/uploader.js"></script>
|
||||
<script type="text/javascript" src="js/lightbox.js"></script>
|
||||
|
||||
<!-- We only want the thunbnails to display when javascript is disabled -->
|
||||
<script type="text/javascript">
|
||||
document.write('<style>.noscript { display: none; }</style>');
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<audio id="betoven" controls autoplay loop autostart="0" src="music/betoven.mp3">
|
||||
HTML5 Audio 를 지원하지 않는 브라우저 입니다
|
||||
</audio> <a id="toggle" href="javascript:toggle()">stop</a>
|
||||
|
||||
<div id="page">
|
||||
<div id="container">
|
||||
<div style="display:flex; align-items:baseline; justify-content:space-between; gap:12px;">
|
||||
<h1 style="margin:0;"><a href="https://ncue.net" target="_blank" rel="noopener noreferrer">NCue</a></h1>
|
||||
<div style="font-size:12px;"><a href="/logout.php">Logout</a></div>
|
||||
</div>
|
||||
|
||||
<div class="gallery-wrap">
|
||||
<div id="gallery" class="content">
|
||||
<div id="controls" class="controls"></div>
|
||||
<div class="slideshow-container">
|
||||
<div id="loading" class="loader"></div>
|
||||
<div id="slideshow" class="slideshow"></div>
|
||||
</div>
|
||||
<div id="caption" class="caption-container"></div>
|
||||
</div>
|
||||
<div id="thumbs" class="navigation">
|
||||
<div id="thumbs-list">
|
||||
<ul class="thumbs noscript">
|
||||
<li>
|
||||
<a class="thumb" name="leaf" href="http://dreamgirl.ncue.net/img/0.jpg" title="Title #0"></a>
|
||||
<div class="caption"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="upload-panel" class="upload-panel">
|
||||
<div class="upload-title">이미지 추가</div>
|
||||
<div id="dropzone" class="dropzone" tabindex="0">
|
||||
<div class="dropzone-text">드래그&드랍 또는 파일 선택</div>
|
||||
<input id="file-input" class="file-input" type="file" accept="image/*" />
|
||||
</div>
|
||||
<div id="upload-preview" class="upload-preview" style="display:none;">
|
||||
<img id="preview-img" alt="preview" />
|
||||
<div id="preview-meta" class="preview-meta"></div>
|
||||
</div>
|
||||
<div class="upload-actions">
|
||||
<button id="upload-btn" type="button" disabled>업로드</button>
|
||||
<span id="upload-status" class="upload-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">© 2009 Trent Foley</div>
|
||||
<div id="lightbox" class="lightbox" style="display:none;" aria-hidden="true">
|
||||
<div class="lightbox-backdrop"></div>
|
||||
<button id="lightbox-close" class="lightbox-close" type="button" aria-label="Close">×</button>
|
||||
<div class="lightbox-content" role="dialog" aria-modal="true">
|
||||
<img id="lightbox-img" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery(function() {
|
||||
if (typeof onLoad === 'function') onLoad();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
55
login.php
Normal file
55
login.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/auth/auth.php';
|
||||
|
||||
dreamgirl_session_start();
|
||||
|
||||
$error = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = isset($_POST['username']) ? (string)$_POST['username'] : '';
|
||||
$password = isset($_POST['password']) ? (string)$_POST['password'] : '';
|
||||
|
||||
if (dreamgirl_check_credentials($username, $password)) {
|
||||
$_SESSION['dreamgirl_user'] = 'admin';
|
||||
header('Location: /index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '아이디 또는 비밀번호가 올바르지 않습니다.';
|
||||
}
|
||||
?><!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Login</title>
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; background:#111; color:#eee; }
|
||||
.wrap { min-height: 100%; display:flex; align-items:center; justify-content:center; padding:24px; box-sizing:border-box; }
|
||||
.card { width: min(420px, 100%); background:#1b1b1b; border:1px solid #2a2a2a; border-radius:12px; padding:18px; }
|
||||
h1 { font-size:18px; margin:0 0 12px; }
|
||||
label { display:block; font-size:12px; color:#bbb; margin:10px 0 6px; }
|
||||
input { width:100%; padding:10px 12px; border-radius:10px; border:1px solid #333; background:#0f0f0f; color:#eee; box-sizing:border-box; }
|
||||
button { width:100%; margin-top:14px; padding:10px 12px; border-radius:10px; border:1px solid #3b82f6; background:#2563eb; color:#fff; cursor:pointer; }
|
||||
.error { margin-top:10px; color:#ff7b7b; font-size:13px; }
|
||||
.hint { margin-top:10px; color:#888; font-size:12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<form class="card" method="post" action="/login.php" autocomplete="off">
|
||||
<h1>로그인</h1>
|
||||
<label for="username">아이디</label>
|
||||
<input id="username" name="username" type="text" required autofocus />
|
||||
<label for="password">비밀번호</label>
|
||||
<input id="password" name="password" type="password" required />
|
||||
<button type="submit">접속</button>
|
||||
<?php if ($error): ?>
|
||||
<div class="error"><?php echo htmlspecialchars($error, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="hint">인증 성공 시 페이지가 표시됩니다.</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
24
logout.php
Normal file
24
logout.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/auth/auth.php';
|
||||
|
||||
dreamgirl_session_start();
|
||||
$_SESSION = [];
|
||||
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(
|
||||
session_name(),
|
||||
'',
|
||||
time() - 42000,
|
||||
$params['path'] ?? '/',
|
||||
$params['domain'] ?? '',
|
||||
(bool)($params['secure'] ?? false),
|
||||
(bool)($params['httponly'] ?? true)
|
||||
);
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
header('Location: /login.php');
|
||||
exit;
|
||||
|
||||
Reference in New Issue
Block a user