[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
コマンドプロンプトでいれる
スペースで切り離したホスト名(www.yahoo.co.jp)とかを指定
Microsoft Windows [Version 10.0.18362.592]
(c) 2019 Microsoft Corporation. All rights reserved.
C:\Users\web.DESKTOP-EQR2U6K>ping yahoo.co.jp
yahoo.co.jp [183.79.135.206]に ping を送信しています 32 バイトのデータ:
192.168.11.1 からの応答: 宛先ホストに到達できません。
192.168.11.1 からの応答: 宛先ホストに到達できません。
192.168.11.1 からの応答: 宛先ホストに到達できません。
192.168.11.1 からの応答: 宛先ホストに到達できません。
183.79.135.206 の ping 統計:
パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
C:\Users\web.DESKTOP-EQR2U6K>
ip ver6が提供するサービスだけが開けてver.4がだめという謎の現象が起こっているらしい
SQLインジェクション
わざとmd5で安全性の低いデータを作ってみますよ
<p><?php echo md5("nobinobi") ?></p>
<!--わざとセキュリティの低いのを出力し、コピー
d0209b61369398a6d2469738c16ffdbf
→
DB→挿入→
INSERT INTO `users` (`u_id`, `pass`, `nickname`) VALUES ('nobita',
'd0209b61369398a6d2469738c16ffdbf', 'のび太');
→サクラ、index.php
を
index2.phpで別名保存
いまindex2.phpはパスワードハッシュしてるので、
<!DOCTYPE html>より上を全部削除
このページは窓口だけにし、認証は別の画面にしましょう
<form method="post" action="auth.php">
と変更
認証ボタンの次の行とかもカット
このページのphpを全部取る形
・u_idとpassが飛んでくるのでそれを処理したい
新規でauth.php
かならずu_idとpassが来ている前提で記述するよ。テストなんで。
脆弱なものをわざとつくるよ
こういう構造
ここまで
--->index2.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ログイン認証</title>
<link rel="stylesheet" href="auth.css">
</head>
<body>
<div id="container">
<form method="post" action="auth.php">
<table>
<tr>
<th><label for="u_id">ユーザー名</th>
<td><input type="text" name="u_id" id="u_id"></td>
</tr>
<tr>
<th><label for="pass">パスワード</label></th>
<td><input type="password" name="pass" id="pass"></td>
</tr>
</table>
<p><button type="submit">認証</button></p>
</form>
</div>
</body>
</html>
--->auth.php
<?php
//いつもの
$user="root";
$dbpass="";
$host="localhost";
$dbname="auth";
$dsn="mysql:host={$host};dbname={$dbname};charset=utf8";
$pdo=new PDO($dsn, $user, $dbpass);
//つくりたいsqlは以下の通り
//SELECT nickname FROM users WHERE u_id='nobita' AND pass='hoehoehoe'";
$sql="SELECT nickname FORM users WHERE u_id='" . $_POST["u_id"] . "'
AND pass='" . $_POST["pass"] . "'";
echo $sql;
?>
たいへんややこしい""の開け閉じ。黄色の部分がかたまりでドットでつないでいる。
--->出力
SELECT nickname FORM users WHERE u_id='nobita' AND pass='aaa';
しかしさきDBに登録したのは脆弱とはいえmd5でハッシュしてるからこのままだと機能ないね
こう書き換えますよ
$pdo=new PDO($dsn, $user, $dbpass);
$pass = md5($_POST["pass"]); //わざとです。脆弱なハッシュ
//つくりたいsqlは以下の通り
//SELECT nickname FROM users WHERE u_id='nobita' AND pass='hoehoehoe'";
$sql="SELECT nickname FORM users WHERE u_id='" . $_POST["u_id"] . "'
AND pass='" . $pass . "'";
echo $sql;
実行結果
SELECT nickname FORM users WHERE u_id='nobita' AND
pass='47bce5c74f589f4867dbd57e9ca9f808'
ちょっと前まではみんなこーゆー危険な書き方をしていたらしい
ぷりぺあーどならぷりぺあーどをつかうが
直にsqlを実行するときはくえりーめそっどをつかうよ
$res=$pdo->query($sql);
>ユーザー入力を受け取ってSQL文を動的に生成する場合は プリペアドステートメント と プレースホルダ を使わなければなりません.
プレースホルダ:
直訳すると「場所取り」.何かユーザ入力を当てはめる場所としてあらかじめ確保しておくもの.
プリペアドステートメント:
直訳すると「予約文」.文を予約したもの.通常,「予約文」は「場所取り」を使うために作られる.もし「場所取り」が無ければ普通に PDO::query などで実行するだけで十分なためである.
fetch
>カーソルをずらしながら,指定したフェッチモードで1行ずつ取得していきます.
引数を省略した場合はデフォルトフェッチモードが使用される.
全ての取得が終わると常に false を返す.
>プレースホルダは、SQL文の中で「ユーザーが入力した値」を代入する箇所に使用します。
例えば以下のようなSQL文があった場合、
INSERT INTO user (user_screen_name) VALUES ('ハルジオン');
末尾にある「ハルジオン」という部分は、ユーザーによって入力される値です。(逆に、他の部分は固定文となります)
プレースホルダを使わずに、上記のようにユーザーから入力された値を直接SQLに結合してしまうと、万が一、悪意のあるユーザーによって不正な値が入力された場合、SQL文が壊れてしまったり、別のSQL文に変えられてしまう恐れがあります。
こういった攻撃手法を「SQLインジェクション」と言います。
これに対策するために、以下のようにプレースホルダを使います。
INSERT INTO user (user_screen_name) VALUES (:user_screen_name);
プレースホルダを使うと、その部分はあくまで「値」として処理し、
SQL文として実行されることはなくなりますので、安全に実行出来るというわけです。
test03.php
<?php
echo str_replace("java","PHP","Hello, java world");
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPエラーコード変換</title>
<link rel="stylesheet" href="test1.css">
</head>
<body>
<h1>phpのエラーメッセージを入力してね</h1>
<form action="mytest01.php" method="get">
<p><input type="textarea" name="hoge" value=""></p>
<button type="submit">send</button>
</form>
</body>
</html>
mytest01.php
<?php
$msg = $_GET["hoge"];
//print_r($msg);
//指定した文字列が一致したら置き換える
$foo=htmlspecialchars($msg,ENT_QUOTES);
$replace = str_replace('Parse error: syntax error', 'あるべき文字が抜けているようだ!', $foo);
$replace = str_replace('Undefined variable', 'ごめんよこの変数見つからないや→', $foo);
//$replace = str_replace('on line', '行数はここだ→', $foo);
//受け取った文字列に,があると認識されなくなる。どうしたらいいかな?
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPエラーコード変換</title>
<link rel="stylesheet" href="test1.css">
</head>
<body>
<h1>エラーコードを変換しました!</h1>
<div class="proto">
<h2><?php echo ("原文"); ?></h2>
<p class="foo"><?php echo $msg; ?></p>
</div>
<div class="arrow"><p>↓</p></div>
<div class="trans">
<h2><?php echo ("変換後"); ?></h2>
<p class="foo"><?php echo $replace; ?></p>
</div>
</body>
</html>
test1.css
body{
width:90%;
margin: 0 auto;
}
input{
width:90%;
margin:auto 0;
height:3rem;
}
.proto p{
color:#ccc;
}
h1{
font-size:1.5rem;
}
.proto h2{
font-size:1rem;
background: #eff;
}
.trans h2{
font-size:1rem;
background: #fee;
}
.proto{
padding: 0;
}
.trans{
padding: 0;
}
.foo{
border:solid 1px #ccc;
border-radius: 3rem;
padding:1.5rem;
}
.arrow{
text-align:center;
font-size: 1.2rem;
color:#ccc;
font-weight:bold;
}
同一ページで書き出して同一ページで処理をするということになっている
if(!empty($_POST["u_id"]) && !empty($_POST["pass"])){
if(!empty($_POST["ticket"]) && $_POST["ticket"]==$_SESSION["ticket"]){
上のif
初めて訪れたのか、認証のためにこのページを訪れたのかで挙動が変わる
下のif
$_POSTデータをもってきて「通りたい」といってきたら、
「チェットみせて」という。
これはアタックを防ぐためにある
if(password_verify($_POST["pass"],$row["pass"])){
プレーンなパスワードと、ハッシュドのパスワードを比較する
この部分がきも。
なお完全にランダムな数はこんぴゅーたーは出力できない
なので、
$ran=rand(100,999);
$salt="tekitounamoji";
$ticket=md5($salt . $ran);
$_SESSION["ticket"] = $ticket;
というふうに
ソルトを使うといい
ブルートアタックに対抗する:
ページを開くたびにランダムな数を生成しチケットを確認する。
input type="hidden"でもたせる。
ブラウザを通してこのページに来た人は、ランダムの値のチケットをゲット
→OK
外部からのアタックはチケットを取れない
またぞろエラーを出した
とりあえずここまで
--->index.php
<?php
session_start();
$msg="";
//初めて開く場合と認証しようと思って開く場合と2種
if(!empty($_POST["u_id"]) && !empty($_POST["pass"])){
if(!empty($_POST["ticket"]) && $_POST["ticket"]==$_SESSION["ticket"]){
//認証開始
$user="root";
$dbpass="";
$host="localhost";
$dbname="auth";
$dsn="mysql:host={$host};dbname={$dbname};charset=utf8";
$pdo=new PDO($dsn, $user, $dbpass);
$sql = "SELECT * FROM users WHERE u_id=:u_id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(":u_id",$_POST["u_id"],PDO::PARAM_STR);
//$stmt->bindValue(":pass",$_POST["pass"],PDO::PARAM_STR);
$stmt->execute();
$row=$stmt->fetch(PDO::FETCH_ASSOC);
if(password_verify($_POST["pass"],$row["pass"])){
//ログイン成功
//$name=$row["nickname"];
$_SESSION["login"]=true;
$_SESSION["name"]=$row["nickname"];
header("Location: mypage.php");
exit();
}else{
//ログイン失敗
$msg="ログインできませんよ";
$_SESSION["login"]=false;
}
}
}
$ran=rand(100,999);
$salt="tekitounamoji";
$ticket=md5($salt . $ran);
$_SESSION["ticket"] = $ticket;
//print_r($_SESSION["ticket"]);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ログイン認証</title>
<link rel="stylesheet" href="auth.css">
</head>
<body>
<div id="container">
<form method="post" action="">
<table>
<tr>
<th><label for="u_id">ユーザー名</th>
<td><input type="text" name="u_id" id="u_id"></td>
</tr>
<tr>
<th><label for="pass">パスワード</label></th>
<td><input type="password" name="pass" id="pass"></td>
</tr>
</table>
<p><button type="submit">認証</button></p>
<input type="hidden" name="ticket" value="<?php echo $ticket; ?>">
</form>
<p><?php echo $msg; ?></p>
</div>
</body>
</html>
--->mypage.php
<?php
session_start();
if(empty($_SESSION["login"])){
header("Location:index.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>マイページ</title>
<link rel="stylesheet" href="auth.css">
</head>
<body>
<div id="container">
<h1 class="hoge"><p>ようこそ<?php echo $_SESSION["name"]; ?>さん!</p></h1>
<p>会員じゃないとみられないクエの絵</p>
<p>あぶらぼうず</p>
<p>たこ</p>
</div>
</body>
</html>
**08
ここのechoが抜けていたので何を押しても知らんふり(エラー)になっていた。echoを忘れるとhiddenで付けられるvalueに値が入らない。ここで「発行」しているので。
<p><button type="submit">認証</button></p>
<input type="hidden" name="ticket" value="<?php echo $ticket; ?>">
</form>
CTRL+Hでキャッシュ削除
まちがってたところはセレクト文:
$sql = "SELECT * FROM users WHERE u_id=:u_id";
この*がちがってた。
1.userテーブルの全部を選択してひっぱってくる
2.dbにあるu_idと、ユーザーが入力しpostされてくる$_POST["u_id"]が一致すればいいんだが、$_POST["u_id"]は「人間が入力してくる」ものだからそのままでは触れない。よってプレースホルダにしておき、あとで中身を説明する。
WHERE句:
>SELECT構文を使って特定のレコードを検索するには、WHERE句を使います。WHEREの後に条件式を指定することにより、その条件にマッチしたレコードだけを選択することができます。
>SELECT * FROM customer WHERE id_c = 1;
全レコードの中からid_cが1のものをひっぱってこいは分かる。
u_idが:u_id"のものをひっぱってこいとはどういう意味だ? :u_idを定義してるのはどこなんだ。
次の授業までにかみ砕けないなら先生にちゃんと聞いて理解するほうがいいと思う。
→
根本的に理解できてなかった。ユーザーの入れて来る文字列はタグがあったり何があるか分からないから、いわば「:保留するぜいぇーい」という記述。:u_idとは。
だからこの時点で定義もへったくれもない。
変数名が同じだと大変混乱するといういつもの癖が出た。「:保留するぜいぇーい」を後解説してるのがbindValue。(のはずだ)
プレースホルダについて
https://webukatu.com/wordpress/blog/1638
sqlインジェクションについて
https://lets.postgresql.jp/documents/tutorial/with_php/against_sql_injection/placeholder/pg_xxxx/1
読んでも全然分からない。
そういうものだ、とりあえずそー記述すりゃいいらしい、とファジーに捉えようと思ったが、phpにしろjavaにしろ「いつか理解できるだろう」とほったらかしのファジーが増えすぎている気がする
さてログイン出来たらマイページに飛ばすよ
しかしページ遷移すると$nameが残らないですね?
①postする。めんどい
②sessionする かしこい
よって、ログイン出来たとき:
//$name=$row["nickname"];
$_SESSION["name"]=$row["nickname"];
先頭にも
<?php
session_start();
$name="";
を記述
ログインしてからはどのページもセッションをつけましょう
というわけで、index.phpにはつけてmypage.phpでsession_start();を付け忘れた例:
**06
出来た例
**07
しかしこれだとログインしてなくてもURLを直打ちしたら見れてしまう。
セッションデータがあるかないかで振り分ける必要がある。
indexの方に追加
//ログイン成功
//$name=$row["nickname"];
$_SESSION["login"]=true;//これを追加!
$_SESSION["name"]=$row["nickname"];
header("Location: mypage.php");
exit();
mypageの方
<?php
session_start();
if(empty($_SESSION["login"])){ //値がないかfalseのとき
//ログインページに戻す
header("Location:index.php");
exit();
}
?>
セッションを消すにはブラウザ終了
クッキーを消してもだめ
戻る進むをおすとまれにセッションが切れることもある
同じ人の同一アクセスが続いているかどうかがセッション情報
森本さんにネットの修復を頼む
だめなら自分の4Gをこのパソコンにつなぐ方法を聞く
ログイン認証を作る
パスワード入力のinputは***になるようにしたい
→<input type="password">
database
users/3カラム
u_id
pass
nickname
はてな?u_idをAIにしてu_nameは別に持つと思ったがそうはしないのか
・パスワード文字数は時代とともに増える性質があるからintの長さは多めにとる
・パスワードはハッシュするのでその意味でも長くしておく理由がある
ハッシュをしなくちゃいけない
ハッシュ専用のphpの関数がある
むかしはmd5(関数名)というのが使われた
md('hoge');
とか。
<?php echo md5("hoge"); ?>
→出力結果
ea703e7aa1efda0064eaa507d9e8ab7e
しかしこれは総当たり的な解析をされて使えなくなった
クリプトというアルゴリズムが考え出された
技術用語でソルトという。ハッシュ+ソルトでハッシュする
自分が好きなキーワードを仕込んで置き、それを組み合わせてハッシュするのでわかりにくくなる
いまはこのソルトを入れるのが常識。
ソルト自身をランダムに作り、しかもハッシュして、それを純粋にパスワードをハッシュしたものに内在させるという最新の手法がうまれた
同じ文字列をハッシュするたびに毎回違う値が出る
password_hash();
<?php echo password_hash("hoge",PASSWORD_DEFAULT); ?>
全部大文字は定数。バージョンがかわってもよろしく動く
第3引数をなしにするとランダムにソルトが生成
出力結果
$2y$10$S5H/GyQ72m8JdxWwMy3Gq.ulJwuaAEDtMVVZlhuw5ThtZqbE8uYba
$2y$10$YrpJTQZFk0zD9s7b.WNAmuSaSLkmYSuiUsfS.eJ1Wr6Un.nRPrGdq
$2y$10$.NTFtovLqwrqWTkKqXM7BOIifMZkww5EoIpBIYxgvTHkBz15wF/d.
みなさんpassをhogehogeで統一するよ
①でてきたものをこぴる
$2y$10$PQbARvt2wZI229dF1vAN9Ob1Tgia2xTkJvZbhq4YglLy0a.lwx8GS
②myadminの「挿入」でそれを入れる
password_hash();
第一 ハッシュする文字列
第二 PASSWORD_DEFAULT(固定)
第三 書かない
検証する関数
password_verify();
引数1 送信された値(平文)
引数2 ハッシュ済み値(DBに登録されているハッシュされたやつ)