-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1040 lines (779 loc) · 100 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.8.0">
<meta charset="utf-8">
<title>Yuchi 的學習筆記</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Yuchi 的學習筆記">
<meta property="og:url" content="https://yuchitung.github.io/index.html">
<meta property="og:site_name" content="Yuchi 的學習筆記">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Yuchi 的學習筆記">
<link rel="alternate" href="/atom.xml" title="Yuchi 的學習筆記" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">Yuchi 的學習筆記</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="https://yuchitung.github.io"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-useful-docker-command" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/07/10/useful-docker-command/" class="article-date">
<time datetime="2020-07-10T09:44:03.000Z" itemprop="datePublished">2020-07-10</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/07/10/useful-docker-command/">常用 docker 指令</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="基本資訊"><a href="#基本資訊" class="headerlink" title="基本資訊"></a>基本資訊</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 顯示 docker 的資訊</span></span><br><span class="line">docker info</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看版本</span></span><br><span class="line">docker -v</span><br><span class="line">docker-compose -v</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 container 佔用資源狀態</span></span><br><span class="line">docker stats</span><br></pre></td></tr></table></figure>
<h2 id="小範例"><a href="#小範例" class="headerlink" title="小範例"></a>小範例</h2><p>假設本機裝好了 docker,但是沒有下載任何的 image,讓我們來跑幾個指令看看如何下載 image,以及啟動容器</p>
<p>範例 1<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 從 docker hub 下載 hello-word image</span></span><br><span class="line">docker pull hello-world</span><br><span class="line"></span><br><span class="line"><span class="comment"># 啟動 hello world cotainer</span></span><br><span class="line">docker run hello-world</span><br></pre></td></tr></table></figure></p>
<p>範例 2<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 這個指令先檢查本機有沒有 ubuntu image,沒有的話會先 pull 再 run</span></span><br><span class="line"><span class="comment"># -i, --interactive (互動模式)</span></span><br><span class="line"><span class="comment"># -t, --tty (配置一個終端機)</span></span><br><span class="line">docker run -it ubuntu bash</span><br><span class="line"></span><br><span class="line">docker run ubuntu /bin/<span class="built_in">echo</span> <span class="string">"Hi, Ubuntu"</span> </span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以一開始在背景執行,後面再用 exec 指令進入環境</span></span><br><span class="line">docker run -d ubuntu </span><br><span class="line"></span><br><span class="line">docker <span class="built_in">exec</span> -it CONTAINER_ID bash</span><br></pre></td></tr></table></figure></p>
<h2 id="Image-相關"><a href="#Image-相關" class="headerlink" title="Image 相關"></a>Image 相關</h2><p>build image<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改 docker-compose.yml 後 rebuild 服務,使用 --no-cache 不使用快取</span></span><br><span class="line">docker-compose build SERVICE_NAME</span><br><span class="line"></span><br><span class="line">docker-compose build --no-cache workspace</span><br></pre></td></tr></table></figure></p>
<p>查看 image<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看機器上有的 image</span></span><br><span class="line">docker images</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 image 的詳細訊息,會返回一個 json 格式</span></span><br><span class="line">docker inspect IMAGE_ID</span><br></pre></td></tr></table></figure></p>
<p>刪除 image<br>注意:必須先刪除有使用這個 image 的 container 才能刪除 image<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 刪除指定 image,</span></span><br><span class="line">docker rmi IMAGE_ID</span><br><span class="line"></span><br><span class="line"><span class="comment"># 找到無用的 images</span></span><br><span class="line">docker images --filter <span class="string">"dangling=true"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 刪除無用的 images</span></span><br><span class="line">docker rmi $(docker images -f <span class="string">"dangling=true"</span> -q)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 刪除名字出現特定字串的 image</span></span><br><span class="line">docker rmi $(docker images |grep <span class="string">'k8s'</span>)</span><br></pre></td></tr></table></figure></p>
<h2 id="Container-相關"><a href="#Container-相關" class="headerlink" title="Container 相關"></a>Container 相關</h2><p>查看 container<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看運行中的 container</span></span><br><span class="line">docker ps</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看所有 container, 包含沒有啟動的(STATUS=exited)</span></span><br><span class="line">docker ps -a</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看停止的 container</span></span><br><span class="line">docker ps -f <span class="string">"status=exited"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 container 的詳細訊息,會返回一個 json 格式</span></span><br><span class="line">docker inspect CONTAINER_ID</span><br><span class="line">docker inspect CONTAINER_NAME</span><br></pre></td></tr></table></figure></p>
<p>啟動或停止 container<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 啟動 container</span></span><br><span class="line">docker start CONTAINER_ID</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重啟 container,如果 container 本身是 stop 狀態,效果同 start</span></span><br><span class="line">docker restart CONTAINER_ID</span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止 container</span></span><br><span class="line">docker stop CONTAINER_ID</span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止在運作的所有 container</span></span><br><span class="line">docker stop $(docker ps -a -q)</span><br></pre></td></tr></table></figure></p>
<p>或是也可以用 docker-compose 一次啟動多個 container<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 docker-compose 啟動服務,-d 為在背景執行</span></span><br><span class="line">docker-compose up -d apache2 mysql</span><br><span class="line">docker-compose up nginx mysql redis</span><br></pre></td></tr></table></figure></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 進入 container</span></span><br><span class="line">docker-compose <span class="built_in">exec</span> --use=root apache2 bash</span><br><span class="line">docker-compose <span class="built_in">exec</span> --use=laradock workspace bash</span><br><span class="line">docker-compose <span class="built_in">exec</span> --user=root mysql bash</span><br><span class="line">docker-compose <span class="built_in">exec</span> --user=root redis bash</span><br></pre></td></tr></table></figure>
<p>刪除 container<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 刪除指定的 container</span></span><br><span class="line">docker rm CONTAINER_ID</span><br><span class="line"></span><br><span class="line"><span class="comment"># 刪除所有 container</span></span><br><span class="line">docker rm $(docker ps -a -q)</span><br></pre></td></tr></table></figure></p>
<p>刪除所有 image & container<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose down</span><br></pre></td></tr></table></figure></p>
<h2 id="log-相關"><a href="#log-相關" class="headerlink" title="log 相關"></a>log 相關</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看某個服務的 log </span></span><br><span class="line">docker-compose logs SERVICE_NAME </span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看 nginx 的即時 log。好用,尤其是在啟動服務時使用了 -d 背景執行,看不到服務的執行狀況時</span></span><br><span class="line">docker-compose logs -f nginx </span><br><span class="line"></span><br><span class="line"><span class="comment"># 看倒數 5 行的 log</span></span><br><span class="line">docker-compose logs --tail 5 nginx </span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以用 docker 指令看倒數 5 分鐘的 log,但對象要是 container id</span></span><br><span class="line">docker logs --since 5m CONTAINER_ID</span><br></pre></td></tr></table></figure>
<h2 id="備份和還原-mysql-container-的資料"><a href="#備份和還原-mysql-container-的資料" class="headerlink" title="備份和還原 mysql container 的資料"></a>備份和還原 mysql container 的資料</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Backup,以 mysqldump 把資料庫備份出來,檔案會存在當前目錄</span></span><br><span class="line">docker <span class="built_in">exec</span> CONTAINERI_ID /usr/bin/mysqldump -u root --password=root DATABASE_NAME > backup.sql</span><br><span class="line"></span><br><span class="line"><span class="comment"># Restore</span></span><br><span class="line">cat backup.sql | docker <span class="built_in">exec</span> -i CONTAINER_ID /usr/bin/mysql -u root --password=root DATABASE_NAME</span><br></pre></td></tr></table></figure>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://docs.docker.com/engine/reference/commandline/cli/" target="_blank" rel="noopener">Use the Docker command line</a></p>
<p><a href="https://ithelp.ithome.com.tw/articles/10186431" target="_blank" rel="noopener">【Day 3】 - Docker 基本指令操作</a></p>
<p><a href="https://blog.kkbruce.net/2019/01/remove-dangling-docker-images-by-name.html#.XkqrjlMzZQI" target="_blank" rel="noopener">如何快速刪除狀態 DANGLING 的特定 DOCKER IMAGES</a></p>
<p><a href="https://gist.github.com/spalladino/6d981f7b33f6e0afe6bb" target="_blank" rel="noopener">Backup and restore a mysql database from a running Docker mysql container</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/07/10/useful-docker-command/" data-id="ckct479dc005in7y5ml2t61d1" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/07/10/useful-docker-command/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/docker/">docker</a></li></ul>
</footer>
</div>
</article>
<article id="post-useful-mysql-command" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/07/09/useful-mysql-command/" class="article-date">
<time datetime="2020-07-09T15:15:26.000Z" itemprop="datePublished">2020-07-09</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/07/09/useful-mysql-command/">常用的 MySQL 指令</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="查看-MySQL-帳號的權限"><a href="#查看-MySQL-帳號的權限" class="headerlink" title="查看 MySQL 帳號的權限"></a>查看 MySQL 帳號的權限</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 連線</span></span><br><span class="line">mysql -u root -p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出所有 user</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="keyword">user</span>,host <span class="keyword">FROM</span> mysql.user;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出目前 user 的權限</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">GRANTS</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出指定 user 的權限</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">GRANTS</span> <span class="keyword">FOR</span> <span class="string">'user'</span>@<span class="string">'host'</span>;</span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">GRANTS</span> <span class="keyword">FOR</span> <span class="string">'root'</span>@<span class="string">'localhost'</span>;</span><br></pre></td></tr></table></figure>
<h2 id="帳號權限的設定"><a href="#帳號權限的設定" class="headerlink" title="帳號權限的設定"></a>帳號權限的設定</h2><p>基本格式:</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>,<span class="keyword">INSERT</span>,<span class="keyword">UPDATE</span>,<span class="keyword">DELETE</span> <span class="keyword">ON</span> DB_NAME.TABLE_NAME <span class="keyword">TO</span> <span class="string">'user_name'</span>@<span class="string">'host'</span> <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'password'</span>;</span><br></pre></td></tr></table></figure>
<p>host 可以是 ‘localhost’ 或是 ‘%’ 表示所有 ip</p>
<p>看一些範例:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 設定 root 可以訪問資料庫 test 的權限,密碼為空</span></span><br><span class="line"><span class="keyword">GRANT</span> ALL <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> test.* <span class="keyword">TO</span> <span class="string">'root'</span>@<span class="string">'%'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 設定 root 訪問全部資料庫的權限,* 表示所有資料庫</span></span><br><span class="line"><span class="keyword">GRANT</span> ALL <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> *.* <span class="keyword">TO</span> <span class="string">'root'</span>@<span class="string">'%'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 設定使用者 tracy 訪問所有資料庫的權限</span></span><br><span class="line"><span class="keyword">GRANT</span> ALL <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> *.* <span class="keyword">TO</span> <span class="string">'tracy'</span>@<span class="string">'localhost'</span> <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'mypwd'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 指定 ip 的訪問權限</span></span><br><span class="line"><span class="keyword">GRANT</span> ALL <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> *.* <span class="keyword">TO</span> <span class="string">'tracy'</span>@<span class="string">'10.2.1.11'</span> <span class="keyword">IDENTIFIED</span> <span class="keyword">BY</span> <span class="string">'mypwd'</span>;</span><br></pre></td></tr></table></figure></p>
<p>改完權限,別忘了更新授權<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FLUSH</span> <span class="keyword">PRIVILEGES</span>;</span><br></pre></td></tr></table></figure></p>
<p>移除 MySQL 帳號權限</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">REVOKE</span> ALL <span class="keyword">PRIVILEGES</span> <span class="keyword">ON</span> *.* <span class="keyword">FROM</span> <span class="string">'tracy'</span>@<span class="string">'localhost'</span>;</span><br><span class="line"><span class="keyword">FLUSH</span> <span class="keyword">PRIVILEGES</span>;</span><br></pre></td></tr></table></figure>
<p>刪除 user<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">USER</span> <span class="string">'testuser'</span>@<span class="string">'localhost'</span>;</span><br></pre></td></tr></table></figure></p>
<h3 id="權限列表"><a href="#權限列表" class="headerlink" title="權限列表"></a>權限列表</h3><p>僅列幾個權限,詳細可參考 <a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html" target="_blank" rel="noopener">官網:6.2.2 Privileges Provided by MySQL</a></p>
<table>
<thead>
<tr>
<th>權限</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td>GRANT OPTION</td>
<td>可把本帳號的許可權授予其它使用者</td>
</tr>
<tr>
<td>LOCK TABLES</td>
<td>鎖定指定資料表</td>
</tr>
<tr>
<td>SHOW DATABASES</td>
<td>可執行 SHOW DATABASES 指令</td>
</tr>
<tr>
<td>CREATE</td>
<td>建立資料庫和資料表</td>
</tr>
<tr>
<td>INDEX</td>
<td>建立或刪除索引</td>
</tr>
<tr>
<td>ALL</td>
<td>所有許可權,但不包括 GRANT</td>
</tr>
<tr>
<td>USAGE</td>
<td>無許可權許可權</td>
</tr>
</tbody>
</table>
<h2 id="遠端連線-MySQL"><a href="#遠端連線-MySQL" class="headerlink" title="遠端連線 MySQL"></a>遠端連線 MySQL</h2><p>在 MySQL 設定裡註解掉 bind-address。</p>
<p>設定檔的位置 /etc/mysql/my.cnf 或是 /etc/mysql/mysql.conf.d</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># bind-address = 127.0.0.1</span><br></pre></td></tr></table></figure>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://support.rackspace.com/how-to/mysql-connect-to-your-database-remotely/" target="_blank" rel="noopener">Connect to a MySQL database remotely</a></p>
<p><a href="https://www.ewdna.com/2011/09/mysqlmessage-from-server-host-xxx-is.html" target="_blank" rel="noopener">無法遠端連接MySQL:message from server: “Host xxx is not allowed to connect to this MySQL server”</a></p>
<p><a href="https://blog.longwin.com.tw/2009/06/query-mysql-show-grant-permission-2009/" target="_blank" rel="noopener">查詢 MySQL 對 此帳號 開放(GRANT)哪些權限</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/07/09/useful-mysql-command/" data-id="ckct479dd005ln7y5xqgixnuu" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/07/09/useful-mysql-command/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/db/">db</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/mysql/">mysql</a></li></ul>
</footer>
</div>
</article>
<article id="post-my-on-board-kit" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/07/09/my-on-board-kit/" class="article-date">
<time datetime="2020-07-09T03:00:47.000Z" itemprop="datePublished">2020-07-09</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/07/09/my-on-board-kit/">On-board Kit</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="homebrew"><a href="#homebrew" class="headerlink" title="homebrew"></a>homebrew</h3><p><a href="https://brew.sh/index_zh-tw" target="_blank" rel="noopener">安裝</a></p>
<h3 id="iTerm2"><a href="#iTerm2" class="headerlink" title="iTerm2"></a>iTerm2</h3><p><a href="https://www.iterm2.com/" target="_blank" rel="noopener">安裝</a> </p>
<h3 id="zsh"><a href="#zsh" class="headerlink" title="zsh"></a>zsh</h3><p><a href="https://medium.com/statementdog-engineering/prettify-your-zsh-command-line-prompt-3ca2acc967f" target="_blank" rel="noopener">超簡單!十分鐘打造漂亮又好用的 zsh command line 環境</a> (包含 iTerm2 安裝)</p>
<h3 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h3><p><a href="https://gitbook.tw/chapters/environment/install-git-in-mac.html" target="_blank" rel="noopener">安裝</a> </p>
<p><a href="https://gitbook.tw/chapters/config/user-config.html" target="_blank" rel="noopener">Git setting</a></p>
<p><a href="https://yuchitung.github.io/2019/04/04/useful-git-command/">常用 git 指令</a></p>
<h3 id="ssh-key"><a href="#ssh-key" class="headerlink" title="ssh key"></a>ssh key</h3><p><a href="https://www.freecodecamp.org/news/the-ultimate-guide-to-ssh-setting-up-ssh-keys/" target="_blank" rel="noopener">The Ultimate Guide to SSH - Setting Up SSH Keys</a></p>
<p><a href="https://medium.com/%E6%B5%A6%E5%B3%B6%E5%A4%AA%E9%83%8E%E7%9A%84%E6%B0%B4%E6%97%8F%E7%BC%B8/how-to-setup-ssh-config-%E4%BD%BF%E7%94%A8-ssh-%E8%A8%AD%E5%AE%9A%E6%AA%94-74ad46f99818" target="_blank" rel="noopener">How to setup SSH config :使用 SSH 設定檔簡化指令與連線網址</a></p>
<p><a href="https://blog.alantsai.net/posts/2016/03/ssh-config-ssh-agent-passphrase-management" target="_blank" rel="noopener">如何用config管理多個網站的ssh key和如何不用每一組輸入ssh的Pass Phrase</a></p>
<h3 id="PhpStorm"><a href="#PhpStorm" class="headerlink" title="PhpStorm"></a>PhpStorm</h3><p>安裝設定:</p>
<p><a href="https://medium.com/@shengyou/2018ironman-eos-for-php-developer-day22-2bda46f25c0d" target="_blank" rel="noopener">簡潔高效的 PHP & Laravel 工作術:從 elementary OS 下手的聰明改造提案 #22</a></p>
<p><a href="https://trello.com/c/wU6MOJLr/77-resolving-issue" target="_blank" rel="noopener">PhpStorm terminal 亂碼</a></p>
<p><a href="https://learnku.com/laravel/t/5420/your-keyboard-shortcuts-please" target="_blank" rel="noopener">PHPStorm 快捷键大全(Win/Linux/Mac)</a></p>
<p>Deployment 設定<br><a href="https://kejyuntw.gitbooks.io/web-developer-learning-resource/software/software-code-editor/phpstorm.html" target="_blank" rel="noopener">SFTP 檔案自動同步</a></p>
<h3 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h3><p><a href="https://docs.docker.com/docker-for-mac/install/" target="_blank" rel="noopener">Docker for Mac 安裝</a></p>
<p><a href="https://medium.com/micheh/%E4%B8%8D%E9%9C%80%E8%A6%81-sudo-%E4%BE%86%E5%9F%B7%E8%A1%8C-docker-%E6%8C%87%E4%BB%A4-612d5a0d4b62" target="_blank" rel="noopener">不需要 sudo 來執行 Docker 指令</a></p>
<p><a href="https://yuchitung.github.io/2020/07/10/useful-docker-command/">常用 docker 指令</a></p>
<h3 id="Laradock"><a href="#Laradock" class="headerlink" title="Laradock"></a>Laradock</h3><p><a href="https://laradock.io/" target="_blank" rel="noopener">安裝</a></p>
<h4 id="env-的設定"><a href="#env-的設定" class="headerlink" title=".env 的設定"></a>.env 的設定</h4><p>程式碼路徑的設定<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">專案程式碼的 local path</span><br><span class="line">APP_CODE_PATH_HOST=../larabbs</span><br><span class="line"></span><br><span class="line">container 裡的 code path</span><br><span class="line">APP_CODE_PATH_CONTAINER=/var/www</span><br></pre></td></tr></table></figure></p>
<p>有關 document root 的設定:</p>
<ul>
<li>apache 預設為 /var/www,像是這樣 APACHE_DOCUMENT_ROOT=/var/www。若是 Laravel 專案就要改成 APACHE_DOCUMENT_ROOT=/var/www/public</li>
<li>nginx 預設為 /var/www/public </li>
</ul>
<p>詳細可以見 </p>
<ul>
<li>laradock/apache2/sites/default.apache.conf</li>
<li>laradock/nginx/sites/default.conf </li>
</ul>
<h4 id="若是要在-nginx-同時架兩個站"><a href="#若是要在-nginx-同時架兩個站" class="headerlink" title="若是要在 nginx 同時架兩個站"></a>若是要在 nginx 同時架兩個站</h4><p>要各別設定 nginx 的設定檔,包含 server_name 和 root。像是下面這樣:</p>
<p>/nginx/sites/site1.conf<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">listen 80;</span><br><span class="line">listen [::]:80;</span><br><span class="line">server_name site1;</span><br><span class="line">root /var/www/site1/public;</span><br></pre></td></tr></table></figure></p>
<p>/nginx/sites/site2.conf<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">listen 80;</span><br><span class="line">listen [::]:80;</span><br><span class="line">server_name site2;</span><br><span class="line">root /var/www/site1/public;</span><br></pre></td></tr></table></figure></p>
<p>如果有遇到 duplicate default server error,要把 default server 的字樣拿掉,詳細可以參考這裡 <a href="https://stackoverflow.com/questions/30973774/nginx-duplicate-default-server-error" target="_blank" rel="noopener">nginx- duplicate default server error</a>。</p>
<p>有關多站點設定可以看這 <a href="https://www.practicemp.com/2019/03/laradock-best-practice.html" target="_blank" rel="noopener">Laradock 最佳实践</a>。</p>
<h4 id="更多教學"><a href="#更多教學" class="headerlink" title="更多教學"></a>更多教學</h4><p><a href="http://jsnwork.kiiuo.com/archives/3151/docker-laradock-%E5%BF%AB%E9%80%9F%E5%AE%89%E8%A3%9D%E7%AD%86%E8%A8%98/" target="_blank" rel="noopener">Docker – Laradock – 快速安裝筆記</a></p>
<p><a href="https://medium.com/%E8%A9%A6%E8%91%97%E7%B4%80%E9%8C%84%E8%87%AA%E5%B7%B1/%E5%AE%89%E8%A3%9Dlaveraldock-5d372ecc03e8" target="_blank" rel="noopener">安裝Laradock</a></p>
<h3 id="資料庫連線工具"><a href="#資料庫連線工具" class="headerlink" title="資料庫連線工具"></a>資料庫連線工具</h3><p>SequelPro <a href="https://sequelpro.com/download" target="_blank" rel="noopener">安裝</a> (MySQL 8.0 連線好像有 bug)</p>
<p>Workbench <a href="https://dev.mysql.com/downloads/workbench/" target="_blank" rel="noopener">安裝</a></p>
<p><a href="https://yuchitung.github.io/2020/07/09/useful-mysql-command/">常用的 MySQL 指令</a></p>
<h3 id="xdebug"><a href="#xdebug" class="headerlink" title="xdebug"></a>xdebug</h3><p><a href="https://yuchitung.github.io/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/">使用 Laradock 和 PhpStorm 設定 xdebug</a></p>
<h3 id="Postman"><a href="#Postman" class="headerlink" title="Postman"></a>Postman</h3><p><a href="https://www.postman.com/downloads/" target="_blank" rel="noopener">安裝</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/07/09/my-on-board-kit/" data-id="ckct479d1004pn7y5r01nbvb5" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/07/09/my-on-board-kit/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/kit/">kit</a></li></ul>
</footer>
</div>
</article>
<article id="post-set-up-xdebug-with-phpstorm-and-laradock" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/" class="article-date">
<time datetime="2020-07-08T14:15:56.000Z" itemprop="datePublished">2020-07-08</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/">使用 Laradock 和 PhpStorm 設定 xdebug</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><ul>
<li>環境: mac</li>
<li>先安裝好 PhpStorm</li>
<li>下載 Laradock 專案</li>
</ul>
<h2 id="安裝-xdebug"><a href="#安裝-xdebug" class="headerlink" title="安裝 xdebug"></a>安裝 xdebug</h2><p>設定 laradock/.env<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WORKSPACE_INSTALL_XDEBUG=true</span><br><span class="line">PHP_FPM_INSTALL_XDEBUG=true</span><br></pre></td></tr></table></figure></p>
<h2 id="設定-xdebug-環境參數"><a href="#設定-xdebug-環境參數" class="headerlink" title="設定 xdebug 環境參數"></a>設定 xdebug 環境參數</h2><p>設定 laradock/workspace/xdebug.ini & laradock/php-fpm/xdebug.ini<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">xdebug.remote_host=docker.for.mac.localhost</span><br><span class="line">xdebug.remote_connect_back=0</span><br><span class="line">xdebug.remote_port=9001</span><br><span class="line">xdebug.idekey=PHPSTORM</span><br><span class="line"></span><br><span class="line">xdebug.remote_autostart=1</span><br><span class="line">xdebug.remote_enable=1</span><br><span class="line">xdebug.cli_color=0</span><br><span class="line">xdebug.profiler_enable=0</span><br><span class="line">xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling"</span><br><span class="line"></span><br><span class="line">xdebug.remote_handler=dbgp</span><br><span class="line">xdebug.remote_mode=req</span><br><span class="line"></span><br><span class="line">xdebug.var_display_max_children=-1</span><br><span class="line">xdebug.var_display_max_data=-1</span><br><span class="line">xdebug.var_display_max_depth=-1</span><br></pre></td></tr></table></figure></p>
<p>rebuild image<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose build --no-cache php-fpm workspace</span><br></pre></td></tr></table></figure></p>
<h2 id="檢查-xdebug-是否正確安裝"><a href="#檢查-xdebug-是否正確安裝" class="headerlink" title="檢查 xdebug 是否正確安裝"></a>檢查 xdebug 是否正確安裝</h2><p>使用 phpinfo() 看看 xdebug 是否開啟了,KEY 是否正確。</p>
<h2 id="設定-PhpStorm"><a href="#設定-PhpStorm" class="headerlink" title="設定 PhpStorm"></a>設定 PhpStorm</h2><p>Preferences -> Languages & Frameworks -> PHP -> Debug</p>
<h3 id="設定-Debug"><a href="#設定-Debug" class="headerlink" title="設定 Debug"></a>設定 Debug</h3><p><img src="https://lh3.googleusercontent.com/pw/ACtC-3cCCLmxAhM5QtNpfVC71FWOibufi_A81pC0gGsrxzDV1SY3FzVKxrLq8PXfFmmZEQuf1K1Y6GAd384Spf2HodXMHc5qOeHJBHm_1xd4UIJgjIDu6BTbDELfj8oM-hf7JDK47k4Z75-9qCgaYVLgxvkDKA=w1574-h1020-no?authuser=0" alt=""></p>
<h3 id="設定-DBGp-Proxy"><a href="#設定-DBGp-Proxy" class="headerlink" title="設定 DBGp Proxy"></a>設定 DBGp Proxy</h3><p><img src="https://lh3.googleusercontent.com/pw/ACtC-3dl-GR76w6TOTmytmvxZJuMkYecSHPl_8nQjQR7ysFuh101EFrNjNohPct56rWN1ZaatjU8RhADSJ4A1fLpA95NjMtP9kmmCdDMoo1T5fq66TYqLK2e-67KdZR9z9z4icPu1rGvdYWeW3hgFZA-c4wieA=w1418-h742-no?authuser=0" alt=""></p>
<h3 id="設定-Servers"><a href="#設定-Servers" class="headerlink" title="設定 Servers"></a>設定 Servers</h3><p>新增一個 server 設定,server name 需和 laradock 中 .env 的 PHP_IDE_CONFIG 設定一致,預設為 laradock</p>
<p><img src="https://lh3.googleusercontent.com/pw/ACtC-3ctJschWrTd7nfxxII2vXI2JzOZl2etpF9ri4P8Z9A0esVyajTcRrSKw2EhrXlsr1Ti7r_h02v1x4Y-9sbYCRB9YCg0E3-U1e57G0g1Sh4zzi2tsP4J1TEMuKBmRgN-2Lelu-glvzTmspqAyug1gGulog=w1980-h853-no?authuser=0" alt=""></p>
<h3 id="設定-remote-debgger"><a href="#設定-remote-debgger" class="headerlink" title="設定 remote debgger"></a>設定 remote debgger</h3><p>點選視窗上方的 ADD CONFIGURATIONS 來新增一個 debugger </p>
<p><img src="https://lh3.googleusercontent.com/pw/ACtC-3eeSy-LSxwDOaZBIsmKwbFe0RqNRXlddL9Su8BgiKp92nfTrT4fqh2y5pdgJH4p05glLC-SJrYcyF4MfIM7xCuyV9p5MonN9GEpbwBqzdk1d68O44-cjcCr1SiF3iXU-vskw8cA9io8VHBVBLwGjeFiuA=w972-h56-no?authuser=0" alt=""></p>
<p>選擇 Templates -> PHP Remote debug,然後照下圖設定<br><img src="https://lh3.googleusercontent.com/pw/ACtC-3fvIUisyhzCQYt6aO7pUf3bf_iHYJz3Z8VCCHxxH27UhmVWWSNN1qwziZoF0nRU8eXM7gsYaBepYjNZoFWz-_u79_1yyFREPyi7GpYyrByvPj0zFXXdplbKBF7qiy6sf-Ml8BkPPagFKspnWUXKN23seA=w1980-h988-no?authuser=0" alt=""></p>
<p>點選 <code>Validate</code> debugger configuration on the Web Server 會出現下圖,然後照著設定<br>注意: 如果是 laravel 專案, Path to create validation script 路徑結尾要設到 public<br><img src="https://lh3.googleusercontent.com/pw/ACtC-3eWpJUpHvprbRD77AyDAus7OqC423AJT5J_3f8b7OIO7k9sgjMnv5unvJC-vltqLKEORhMWdM21WDQhhKD0PwCU0uodi5X3UpB8lZ_Z8WnJcAS1TLPPJrsMJ90CMVxkQ8xZLO_Qxc1gT0he5vogRXaFGQ=w1104-h952-no?authuser=0" alt=""></p>
<p>設定完後按 <code>VALIDATE</code>,如果沒有什麼錯誤訊息,應該就是可以用了。<br><img src="https://lh3.googleusercontent.com/pw/ACtC-3eAHenJiCpU_rOwHp24zD2_jXfE07AFoymBEUEnJWFw1sXL5yDH9zkc6keUr5aQO5GJLsHV2nHUYhvmq1WdtpXvssSAUGULpr0bKdbCTpLYgOVv1afnxU2LxYQgvhmjRA48qPFK5fls2YAEbm2UIKx2dQ=w1098-h946-no?authuser=0" alt=""></p>
<h2 id="幾個遇過的坑點"><a href="#幾個遇過的坑點" class="headerlink" title="幾個遇過的坑點"></a>幾個遇過的坑點</h2><p>基本上上面的設定都是測試可用的作法,下面算是曾經遇過的 issue,有遇到再看就好了。</p>
<h3 id="port-的設定"><a href="#port-的設定" class="headerlink" title="port 的設定"></a>port 的設定</h3><p>php-fpm 的 port 為 9000,xdebug port 就不能設定為 9000 , 所以改設 9001,<br>參考:<a href="https://thecodingmachine.io/configuring-xdebug-phpstorm-docker" target="_blank" rel="noopener">DEBUGGING PHP (WEB AND CLI) WITH XDEBUG USING DOCKER AND PHPSTORM</a>。</p>
<h3 id="remote-connect-back-的設定"><a href="#remote-connect-back-的設定" class="headerlink" title="remote_connect_back 的設定"></a>remote_connect_back 的設定</h3><p>這個設定要改成 0,就是要關掉啦。</p>
<p>官網有說明這個參數的用途,基本上就是多人開發時使用:</p>
<blockquote>
<p> There is also a xdebug.remote_connect_back setting that can be used if your development server is shared with multiple developers.</p>
</blockquote>
<blockquote>
<p>If enabled, the xdebug.remote_host setting is ignored and Xdebug will try to connect to the client that made the HTTP request. It checks the $_SERVER[‘HTTP_X_FORWARDED_FOR’] and $_SERVER[‘REMOTE_ADDR’] variables to find out which IP address to use.</p>
</blockquote>
<p>更多訊息可以看官網<a href="https://xdebug.org/docs/all_settings#remote_connect_back" target="_blank" rel="noopener">說明</a>。</p>
<p>更多介紹可以看<a href="http://guojianxiang.com/posts/2015-09-06-PHP_Debug_Tool-Xdebug.html" target="_blank" rel="noopener">PHP调试工具-Xdebug</a></p>
<h3 id="composer-超慢"><a href="#composer-超慢" class="headerlink" title="composer 超慢"></a>composer 超慢</h3><p>image build 完重啟後,composer install 一直無反應 ,連 composer -v 也是如此,以為是 container 有問題,所以反覆刪掉 container 和 image,反覆重 build 重啟…。 </p>
<p>直到放了一段很長時間後,composer 才開始跑,並且在跑之前有這樣的訊息:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1571381068.831664: There was a problem sending 289 bytes on socket 4: Broken pipe</span><br></pre></td></tr></table></figure>
<p>經過超多次測試,確定是開了 debug 造成 composer 超慢,也嘗試了超多種設定,目前還沒有找到一個最佳解。<br>目前暫時的做法是:</p>
<ol>
<li>docker 的 xdebug 設定開啟 </li>
<li>然後 container 啟動後以 root 權限進入 workspace, 註解掉 /etc/php/7.2/cli/con.d/20-xdebug.ini 裡面的設定,<br>這樣可以暫時讓兩個服務都能使用…</li>
</ol>
<p><code>UPDATE</code>: 20200708 再次測試,已經沒有這個問題。</p>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://learnku.com/articles/24389" target="_blank" rel="noopener">Laradock 使用 PhpStorm Debug 代码</a></p>
<p><a href="https://segmentfault.com/a/1190000011387666" target="_blank" rel="noopener">使用 Xdebug 在 PHPStorm 中调试 PHP 程序(框架/原生均适用)</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/" data-id="ckct479da005dn7y58d03k18l" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/PhpStorm/">PhpStorm</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/env/">env</a></li></ul>
</footer>
</div>
</article>
<article id="post-cgi-fastcgi-and-phpfpm" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/07/06/cgi-fastcgi-and-phpfpm/" class="article-date">
<time datetime="2020-07-06T09:02:14.000Z" itemprop="datePublished">2020-07-06</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/07/06/cgi-fastcgi-and-phpfpm/">CGI、FastCGI 和 PHP-FPM</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>早期的網頁只是按照客戶端請求將保存在 web server 中的靜態資源回傳(例如圖片、CSS、HTML),這種情況下客戶端每次獲取的訊息都是同樣的內容,而現今僅僅通過靜態資源已經無法滿足客戶的需求,所以引入 CGI 以便客戶端請求能夠觸發 web server 運行另一個外部程式,連同客戶端所輸入的參數一起傳給這個外部程式,該程式會將動態生成的 HTML 和其他資訊通過 web server 再返回給客戶端(即動態請求,例如基於 PHP、Python、Java 實現的應用)。利用 CGI 可以針對用戶請求動態返回給客戶端各種各樣動態變化的訊息。</p>
<p>以 PHP 來說,PHP 的運作模式分成幾種:</p>
<ul>
<li>CLI命令行模式</li>
<li>Module 模式</li>
<li>CGI 模式 </li>
<li>Fast-CGI 模式</li>
</ul>
<p>這篇只先討論下面兩種模式,有興趣暸解 Module 模式的可以看 <a href="https://mark-lin.com/posts/20190131/" target="_blank" rel="noopener">PHP 的 Web 運行原理 ( 1 ) - 傳統型</a>。</p>
<h2 id="CGI"><a href="#CGI" class="headerlink" title="CGI"></a>CGI</h2><p>CGI(Common Gateway Interface),為 web server 與 CGI 程式(如 php, python)間進行“交談”的一種<code>工具或協議</code>。CGI 程式可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環境變量,如 php、perl、tcl 等。</p>
<p>根據使用者的請求,web server 會傳一些資訊給 PHP 解析器,例如 URL、查詢字串、POST 內容、HTTP header。CGI 就是規定要傳哪些資訊,以什麼樣的格式傳遞給後方處理這個請求的協議。web 服務器收到用戶請求,就會把請求提交給 CGI 程式(如php-cgi),CGI 程式根據請求的參數來解析程式,然後輸出標準的 html 語句,返回給 web 服務器, web 服務器再返回給客戶端,這就是 CGI 的工作流程,可以參考下圖。</p>
<p><img src="https://www.elprocus.com/wp-content/uploads/CGI-WORKING-scaled.jpg" alt="common-gateway-interface-working"><br><a href="https://www.elprocus.com/what-is-common-gateway-interface-working-and-its-applications/" target="_blank" rel="noopener">圖片來源</a></p>
<p>CGI program 針對每個 HTTP 請求都會 fork 一個新的 process 來做事,例如解析配置文件、初始化執行環境、處理請求等,然後把這個 process 處理完的結果通過 web server 轉發給用戶,剛剛 fork 出的 process 也隨之退出,如果下次用戶再請求動態資源,那麼 web server 又再次 fork process,如此周而復始,過程大概如下。</p>
<p>CGI program 的工作流程:</p>
<ol>
<li>初始化各種相關變量</li>
<li>調用並初始化 zend 虛擬機</li>
<li>載入並解析 php.ini</li>
<li><ul>
<li>啟動 zend, zend 載入 php 腳本,做語法分析</li>
<li>編譯 php 腳本成 opcode</li>
<li>輸出結果</li>
<li>關閉虛擬機</li>
</ul>
</li>
<li>回傳結果給 web server</li>
</ol>
<p>傳統 CGI 主要缺點是效能很差,因為每一次 web 請求都會有啟動和退出的過程,也就是 fork-and-execute 模式。這個模式幾乎無法處理高併發的請求,因此就誕生了 FastCGI。此外,傳統的 CGI 安全性也很差,現在已經很少被使用了。</p>
<h2 id="FastCGI"><a href="#FastCGI" class="headerlink" title="FastCGI"></a>FastCGI</h2><p>FastCGI 是 CGI 的升級版本,為了提升 CGI 的效能而生,也是種協議。與 CGI 的 fork-and-execute 模式不同,FastCGI 則會先 fork 一個 master process,解析配置文件,初始化執行環境,然後再 fork 多個 worker process,當請求過來時 master process 會將請求分配給一個 worker process,然後立即可以接受下一個請求,提高了處理請求的效率,而且當 worker process 不夠用時,master process 還可以根據配置預先啟動幾個 worker process 等著,當閒置的 worker process 太多時,也會關掉一些,這樣不僅提高了效能,也節省了系統資源。另外常佇運行的 master process 同時也避免了重複的初始化操作。</p>
<p>FastCGI 採用 client-server 結構,可以將 web server 和腳本解析服務器分開,同時在腳本解析服務器上啟動一個或者多個腳本解析 process。當 web server 每次遇到動態程序時,可以將其直接交付給 FastCGI process 來執行,然後將得到的結果返回給瀏覽器。這種方式可以讓 web server 專注處理靜態請求,或者將動態腳本服務器的結果返回給客戶端,提高了整個應用系統的性能。</p>
<p><img src="https://lh3.googleusercontent.com/MsL2GIo-A9fgPiHt_yE93vq1_FsfEjIlmekcT3Gfe1y6QDbDKJFKycdCCxU5OWkVE_IAf6_Mt4fE15yc26l9JYcdElv3juIloAY0VTioIpw8miHt7X5O7MdwiUrcGT6kwO6tcoSm0M8MlyjNZzek1cij_RS-zGUMKEMSIYo4ym-uSvLAB7ywVBtO1H5VoADV0DmvsYfvMBe9xflVP2wBBiSWHPK-bRNiMcp70BFhyO15qJ-UlDXe6Lo4DnZ4MzyzyPllJ1grKs00BJYdZmdoEtpYzUrh0fur-M5050CS9Kkv5hCXjduzPzKZ1l4fSsq3WX3McWy7u4icuw6SwoJWlm5mM-PhOG5iHWY0PpgrmzycsoFgULJ1NqsGzerBowkFOc4LzfQd8KFo-LmAu_KF9VXyPthEDBtxifmEGGtSjrnIvQY-Cu51gskU3L9QklXAW1m4hZxvpOXbWsoQTC-FVDXSRKr_YCX1pS6iwyc1aBluacOjQe1etzQ8aDUY2jM6T3CNOrhVJM9kJWdfAKiPY4haj8GsZhreSVTiCmepis-NSYF3ysJpq3syFBNUPI7f8d6Nb2AaQlnwXdhHyUMFym8SJenLtB7UNYqLd1EVQWK3RqjhfkyUT33oDcYYK5dfxNooMcL0HJQj-ATqx7bP08ZxZ84-fHga_pWD0NUTDZ-FquuC9jPPVRTQ2xZ5AwM=w851-h282-no?authuser=0" alt=""></p>
<h2 id="PHP-FPM"><a href="#PHP-FPM" class="headerlink" title="PHP-FPM"></a>PHP-FPM</h2><blockquote>
<p>PHP-FPM (php-Fastcgi Process Manager) 是對於 FastCGI 協議的具體實現。</p>
</blockquote>
<p>FastCGI 只是一個協議規範,需要每個語言具體去實現,PHP-FPM 就是 PHP 版本的 FastCGI 協議的具體實作,目的為實現 web server 與 PHP 腳本之間的溝通。</p>
<p>PHP-FPM 負責管理一個 process pool 來處理來自 web server 的 HTTP 動態請求,在 PHP-FPM 中,master process 負責與 Web server 進行通訊,接收 HTTP 請求,再將請求轉發給 worker process 進行處理,worker process 主要負責動態執行 PHP 程式碼,處理完成後,將處理結果返回給 web 服務器,再由 web 服務器將結果發送給客戶端。這就是PHP-FPM 的基本工作原理,概念可以看下圖:</p>
<p><img src="https://lh3.googleusercontent.com/ONExjhRMGPOeI6gN8j_GcBHEc_zXXtQKCgZuhkm3mQlgz0fdh-T07xIyoKfSUIl2i9zW9ZPGPd7PooGjS4Z-n8UIdM2Koexnw6TSMN5LUuVl4vMKuorj8O4XaXc2qJ1hrT0mdlOtLHPLd1W3ea9LFF_zOJSzBPwbM6QbRWSZa93ew-ittpCgLwNrOLyweXcHp--D2MWxObKZXM48LNU38AaWQ0ktIxkRbCq22ku54wmqEAs3fPeNpXUNwxNQuprWoHiE8SoamylD9xub-3884Ui8ZOtXGFtrPLqIeUoGXDJ4PzV4QI03R11V0TC3W1CMICXQJ1aSbLPodLgettvFk2RHin5C1PiGoPhMbyGK6T060X1zTvuhh61J5SuRyjvhfbTYLuTa0mDDUZPaWOMcdLiD-5Ql8PGv9sVEYDGsEN5h3rD9Le7yuluJLWKfrTk2-2uurbf-Lb8il4HGG93k05fkayG7I3D42xlbiUHWQKCpsrOa-caAo2hl1htnbxh2rsK-nKvY4FbAlTDweXO_e6kB_otj1wS2GGCuEEkp6NGJTPaufqC-x4Ivqd4rhmIQ99mebOCh4pnycebY_eGkxYCRYZ_LZUEg74OcT_KHfj43g4CQrughO-GMlNVItGiEA0zN9z7_f3mjYp3ACvnm3oVUZypzlmtSrVA2NjtOb7h9zKvt0mX-nvcRzF77YMI=w1242-h462-no?authuser=0" alt=""></p>
<h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><ul>
<li>CGI: 介於 HTTP server 與 CGI program 溝通的工具、協定。</li>
<li><p>FastCGI: CGI 的改版,其 process 會持續運行,並支援分佈式運行。</p>
</li>
<li><p>PHP-CGI: 一種實作 FastCGI 的 process manager</p>
</li>
<li>PHP-FPM: 另一種實作 FastCGI 的 process manager,原本是 PHP 的補丁,在 PHP 5.3.2 被官方加入 PHP。 </li>
</ul>
<p>最後附上幾篇文章討論關於 PHP-FPM 的優化建議:</p>
<p><a href="https://clhjoe.github.io/post/php-process-tuning/" target="_blank" rel="noopener">PHP Process Tuning</a></p>
<p><a href="http://www.stelin.me/2017/06/09/php-fpm%E8%AF%A6%E8%A7%A3" target="_blank" rel="noopener">php-fpm详解</a> (這篇有討論關於 502 Bad Gateway & 504 Gateway Time-out 的優化建議)</p>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3" target="_blank" rel="noopener">通用閘道器介面</a></p>
<p><a href="https://goodspeedlee.blogspot.com/2016/08/it-cgi.html" target="_blank" rel="noopener">IT 考古: 什麼是 CGI?</a></p>
<p><a href="https://www.awaimai.com/371.html" target="_blank" rel="noopener">CGI、FastCGI和PHP-FPM关系图解</a></p>
<p><a href="https://www.elprocus.com/what-is-common-gateway-interface-working-and-its-applications/" target="_blank" rel="noopener">What is Common Gateway Interface : Working and Its Applications</a></p>
<p><a href="https://six.club/article/403" target="_blank" rel="noopener">PHP-FPM是什么?</a></p>
<p><a href="https://www.php.net/manual/en/install.fpm.php" target="_blank" rel="noopener">FastCGI Process Manager (FPM)</a></p>
<p><a href="https://aleen42.gitbooks.io/wiki/qa/cgi_difference.html" target="_blank" rel="noopener">Differences between CGI, FastCGI, PHP-CGI, PHP-FPM, Spawn-FCGI </a></p>
<p><a href="https://mark-lin.com/posts/20190131/" target="_blank" rel="noopener">PHP 的 Web 運行原理 ( 1 ) - 傳統型</a></p>
<p><a href="https://ifun01.com/BLLAFS9.html" target="_blank" rel="noopener">CGI、FastCGI、PHP-CGI與PHP-FPM</a></p>
<p><a href="https://www.twblogs.net/a/5d0043fabd9eee14029fb688" target="_blank" rel="noopener">php-fpm與fastcgi、php-cgi之間的關係及源碼解析</a></p>
<p><a href="https://itw01.com/Q34X5EA.html" target="_blank" rel="noopener">【PHP核心】PHP的執行模式</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/07/06/cgi-fastcgi-and-phpfpm/" data-id="ckct479co003zn7y5o5alo82q" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/07/06/cgi-fastcgi-and-phpfpm/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/php/">php</a></li></ul>
</footer>
</div>
</article>
<article id="post-csrf-introduction" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2020/04/21/csrf-introduction/" class="article-date">
<time datetime="2020-04-21T11:11:03.000Z" itemprop="datePublished">2020-04-21</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/04/21/csrf-introduction/">CSRF 介紹</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="CSRF-是什麼?"><a href="#CSRF-是什麼?" class="headerlink" title="CSRF 是什麼?"></a>CSRF 是什麼?</h2><p>CSRF(Cross Site Request Forgery) 為跨站請求偽造。簡單講,被害者點擊了攻擊者提供的網頁後,網頁裡面埋了一些惡意的程式碼,<br>目的為假冒被害者的身份去對目標網站發出請求,請求可以是刪除資源或是轉帳等動作,通常是發生在目標網站使用 cookie 做身份認證的情況,利用 cookie 會隨著請求一起被送出的特性來達到攻擊的效果。</p>
<h2 id="解法"><a href="#解法" class="headerlink" title="解法"></a>解法</h2><ul>
<li>SameSite cookie</li>
<li>Referer</li>
<li>CSRF Token</li>
<li>Double Submit Cookie</li>
</ul>
<h3 id="SameSite-cookie"><a href="#SameSite-cookie" class="headerlink" title="SameSite cookie"></a>SameSite cookie</h3><p>此做法是在 server 設定 cookie 時,加上 SameSite 設定,表示只有在同網站的情況下瀏覽器才隨請求發送 cookie 給 server。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-Cookie: session_id=zxcvbn19rtyu89; SameSite</span><br></pre></td></tr></table></figure>
<p>但是有幾個缺點:</p>
<ul>
<li>不是所有瀏覽器版本都支援,使用前須先確認</li>
<li>對於如果是從 Google 搜尋頁或是別人分享的連結來點擊進入網頁,會無法保持登入的效果,對使用者體驗不利</li>
</ul>
<p>解法有兩種:</p>
<ul>
<li>設定兩種類型的 cookie<br>把 cookie 分成兩種,一種是維持登入狀態用的 cookie,不設為 SameSite,讓點擊連結進入網站時仍然對 server 發送 cookie,讓使用者保持登入狀態;<br>另一種是敏感操作時候用的 cookie,要設為 SameSite,避免偽造請求的攻擊。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Set-Cookie: session_id=zxcvbn19rtyu89 </span><br><span class="line">Set-Cookie: name=secret; SameSite</span><br></pre></td></tr></table></figure>
<ul>
<li>使用 Lax & Strict<br>先簡單介紹一下 SameSite 的幾種設定:Strict、Lax、None。</li>
</ul>
<p>Strict:要求最嚴格,只有 same site 的情況下,才會送出 cookie。<br>Lax:比 Strict 寬鬆一點,除了 GET 請求以外,不發送 cookie 到第三方。<br>None: 允許所有網站對 cookie 的存取。</p>
<p>所以我們可以使用 SameSite=Lax,讓 POST、PUT、DELETE 請求在跨域的情況下不發送 cookie,避免 CSRF 攻擊,又可以在透過點擊連結進入網站時保持登入狀態。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-Cookie: session_id=zxcvbn19rtyu89; SameSite=Lax</span><br></pre></td></tr></table></figure>
<p>詳細的細節可以看看 <a href="https://tools.ietf.org/html/draft-west-first-party-cookies-07" target="_blank" rel="noopener">RFC 對於 SameSite cookie 的規範</a></p>
<h3 id="Referer"><a href="#Referer" class="headerlink" title="Referer"></a>Referer</h3><p>檢查 request header 的 referer,看 domian 是否合法來避掉偽造請求。<br>但是有風險:</p>
<ul>
<li>瀏覽器可能不支援</li>
<li>使用者也可以關閉 referer 的傳送,這樣就會遺失真正的請求</li>
</ul>
<p>如果對 referer 不太暸解可以看<a href="http://www.ruanyifeng.com/blog/2019/06/http-referer.html" target="_blank" rel="noopener">HTTP Referer 教程</a>。</p>
<h3 id="CSRF-Token"><a href="#CSRF-Token" class="headerlink" title="CSRF Token"></a>CSRF Token</h3><p>最常見的解法,許多框架也直接支援這個做法。</p>
<p>手法為在 session 和 form 裡面直接埋入一個 CSRF token,然後 server 收到請求後比對是否相同,偽造請求因為不會有這個 CSRF token,所以可以避免造假攻擊,但是缺點為 session 也要存一份,無法 stateless。</p>
<h3 id="Double-Submit-Cookie"><a href="#Double-Submit-Cookie" class="headerlink" title="Double Submit Cookie"></a>Double Submit Cookie</h3><p>Double Submit Cookie 是 CSRF token 的變形,一樣是要產出一組 CSRF token,但是改存在 cookie 裡面,作法也分兩種:</p>
<ul>
<li><p>由 server 產生 token:<br>server 產生 token 後,不存在 session,改存在 cookie 裡,然後 form 也一樣埋入這個 token,等 server 收到請求後一樣把兩者做比對,因為偽造攻擊的網頁裡面的 form ㄧ定沒有這個 token,所以比對後會發現是非法的請求。</p>
</li>
<li><p>由 client 產生 token:<br>client 產生 token 後,同時設定在 cookie 裡和 header 上,等 server 收到請求後一樣把兩者做比對,概念是因為攻擊者無法偽造出目標網站的 cookie 。</p>
</li>
</ul>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://blog.techbridge.cc/2017/02/25/csrf-introduction/" target="_blank" rel="noopener">讓我們來談談 CSRF</a></p>
<p><a href="https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0" target="_blank" rel="noopener">wiki:跨站請求偽造</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2020/04/21/csrf-introduction/" data-id="ckct479cs0047n7y5195u364x" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2020/04/21/csrf-introduction/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/web/">web</a></li></ul>
</footer>
</div>
</article>
<article id="post-where-to-store-json-web-token" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2019/07/14/where-to-store-json-web-token/" class="article-date">
<time datetime="2019-07-14T15:12:55.000Z" itemprop="datePublished">2019-07-14</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/07/14/where-to-store-json-web-token/">JWT 到底要存哪裡?</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>之前討論過關於 JWT 的概念跟使用方法,但是關於 JWT 的儲存方式只講了一般的用法,其實這一部分在網路上眾說紛紜,今天就來聊聊。</p>
<h2 id="存在-web-storage"><a href="#存在-web-storage" class="headerlink" title="存在 web storage"></a>存在 web storage</h2><p>上一篇講到 JWT 是以 header 的方式傳給 server,這個是指把 JWT 存在瀏覽器的 web storage(local storage 或 session storage)上。<br>這種做法需要注意的幾件事:</p>
<ul>
<li>不會有 CSRF 危險</li>
<li>可能會有 XSS 攻擊的疑慮,因為 JWT 可以透過 js 來取得。</li>
</ul>
<h2 id="存在-cookie"><a href="#存在-cookie" class="headerlink" title="存在 cookie"></a>存在 cookie</h2><p>也有人把 JWT 存在 cookie 裡,存在 cookie 的話可能要注意的幾件事:</p>
<ul>
<li>需要注意 CSRF 的風險,但是現代前端框架已經開始支援保護 cookie。</li>
<li>為了防止 XSS 攻擊,要設定 HttpOnly ,防止由 javascript 取得 cookie,詳見<a href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Cookies#Secure_%E4%BB%A5%E5%8F%8A_HttpOnly_cookies" target="_blank" rel="noopener">MDN:HTTP cookies。
</a></li>
<li>如果有跨域需求,要在 header 加上 domain 資訊,但有可能有瀏覽器版本不支援的問題。</li>
</ul>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>關於 JWT 要存在哪到現在還是各有各的說法,沒有一個定論,我想無論如何實作,知道分別的優缺點以及針對需要注意的資安風險做出對應的保護是必須的。 </p>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage" target="_blank" rel="noopener">Where to Store your JWTs – Cookies vs HTML5 Web Storage</a></p>
<p><a href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Cookies" target="_blank" rel="noopener">MDN:Http Cookies</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2019/07/14/where-to-store-json-web-token/" data-id="ckct479dg005qn7y59aqqpe97" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2019/07/14/where-to-store-json-web-token/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/web/">web</a></li></ul>
</footer>
</div>
</article>
<article id="post-introduction-to-json-web-token" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2019/07/10/introduction-to-json-web-token/" class="article-date">
<time datetime="2019-07-10T13:50:51.000Z" itemprop="datePublished">2019-07-10</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/07/10/introduction-to-json-web-token/">淺談 JWT (Json Web Token)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>當服務開始龐大,server 開始 scale out,client 端每次的請求可能會交由不同的 server 處理,傳統的 session 模式必須解決 session consistency 的問題,常見的用法可以使用 Memcached 或是 Redis server 來管理。而 JWT 則提供了另一種身份認證的解決方案,有別於 session 具狀態性(Stateful)的形式,jwt 是無狀態的(Stateless),也就是不用考慮今天請求被分配到哪一台 server,根據隨著請求而來的 token ,server 就可以知道 client 是誰。 </p>
<p>client 使用帳號密碼登入或是第三方登入,經 server 認證其身份,驗證成功後回傳一組 JWT 給 client,之後 client 在發起請求時都應夾帶這組 JWT 給 server 核查身份。過程中 server 都不用向資料庫發出查詢,也不用額外紀錄或是開啟 session,並且任何 server 拿到 token 都可以辨別其身份,過程如下圖:</p>
<p><img src="https://cdn.auth0.com/content/jwt/jwt-diagram.png" alt="JWT 驗證流程"></p>
<p>JWT 的組成分成3個部份:header、payload、signature,結構如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Header.Payload.Signature</span><br></pre></td></tr></table></figure>
<h3 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h3><p>header 夾帶兩個資訊,一個是類型,這裡就是指 JWT,第二個為使用的演算法,如 HMAC、SHA256、RSA。</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"alg"</span>: <span class="string">"HS256"</span>,</span><br><span class="line"> <span class="attr">"typ"</span>: <span class="string">"JWT"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>然後將它用 base64 編碼,此為 JWT 的第一部份。</p>
<h3 id="Payload"><a href="#Payload" class="headerlink" title="Payload"></a>Payload</h3><p>payload 用來存放需要傳遞的資料,<a href="https://tools.ietf.org/html/rfc7519#section-4.1" target="_blank" rel="noopener">RFC7519</a> 列有7個官方推薦的參數,但是這些為非必要項目,沒有也沒關係。</p>
<table>
<thead>
<tr>
<th>變量名</th>
<th>英文全寫</th>
<th>備註</th>
</tr>
</thead>
<tbody>
<tr>
<td>iss</td>
<td>Issuer</td>
<td>JWT 的核發者</td>
</tr>
<tr>
<td>sub</td>
<td>Subject</td>
<td>JWT 的主體或者用戶</td>
</tr>
<tr>
<td>aud</td>
<td>Audience</td>
<td>接收 JWT 的用戶</td>
</tr>
<tr>
<td>exp</td>
<td>Expiration</td>
<td>Time 過期時間(單位為秒)</td>
</tr>
<tr>
<td>nbf</td>
<td>Not Before</td>
<td>開始時間(單位為秒),在該時間之前無效</td>
</tr>
<tr>
<td>iat</td>
<td>Issued At</td>
<td>發佈時間(單位為秒)</td>
</tr>
<tr>
<td>jti</td>
<td>JWT ID</td>
<td>JWT唯一標識,區分不同發布者的統一的標識</td>
</tr>
</tbody>
</table>
<p>除此之外,也可以自己定義要夾帶的其他資訊,例如:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"sub"</span>: <span class="string">"1234567890"</span>,</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"John Doe"</span>,</span><br><span class="line"> <span class="attr">"admin"</span>: <span class="literal">true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>再來,一樣也是把 payload 用 base64 編碼,此為 JWT 的第二個部份。</p>
<h3 id="Signature"><a href="#Signature" class="headerlink" title="Signature"></a>Signature</h3><p>第三個來做簽章(signature),簽章是 JWT 的核心,是為了避免有人更改 payload ,偽造身份發出請求,除非 secret 外洩,否則偽造者無法做出合法的簽章。作法是使用剛剛 <code>base64 後的 header</code> 以及 <code>base64 後的 payload</code> 和一個只有 server 才知道的 <code>secret</code> ,使用的演算法就是 header 裡面指定的演算法,作法如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)</span><br></pre></td></tr></table></figure>
<p>最後把 header、payload、signature 用 <code>.</code>組起來,JWT 就完成了,可以直接來個<a href="https://jwt.io/" target="_blank" rel="noopener">線上測試</a>。</p>
<h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><p>client 發起請求的時候,header 需夾帶下面的資訊才能通過身份驗證。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Authorization: Bearer <token></span><br></pre></td></tr></table></figure>
<h2 id="注意事項"><a href="#注意事項" class="headerlink" title="注意事項"></a>注意事項</h2><ul>
<li>JWT 代表了一個人的認證身份,任何人拿到它都可以盜用其身份對 server 發出請求,故 token 應該安全保存,並且存活的有效期不應設太長,對於一些比較重要的應用應該要再驗證一次身份。</li>
<li>任何人拿到 token 都可以解析 payload 的內容,除非有額外做加密處理,否則不應夾帶敏感資訊。</li>
<li>一但核發出去的 token ,除非後端有額外的機制,否則無法註銷,只能等待其過期。</li>
<li>根據上一點,故也無法實踐單點登入。</li>
<li>應結合SSL使用。</li>
<li>不要重複造輪子,善用官方建議的 <a href="https://jwt.io/" target="_blank" rel="noopener">Library</a>。</li>
</ul>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>JWT 不是萬靈丹,有其優點也有其缺點,端看應用是否適合,並做好安全性的防護。<br>有關 JWT 的儲存方式各有各的說法,下次再開一篇新章討論。 </p>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://jwt.io/introduction/" target="_blank" rel="noopener">Introduction to JSON Web Tokens</a><br><a href="http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html" target="_blank" rel="noopener">JSON Web Token 入門教程</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2019/07/10/introduction-to-json-web-token/" data-id="ckct479cy004mn7y5ftmvt3df" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2019/07/10/introduction-to-json-web-token/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/web/">web</a></li></ul>
</footer>
</div>
</article>
<article id="post-what-is-cross-site-scripting-and-how-to-prevent-it" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2019/07/03/what-is-cross-site-scripting-and-how-to-prevent-it/" class="article-date">
<time datetime="2019-07-03T12:45:18.000Z" itemprop="datePublished">2019-07-03</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/07/03/what-is-cross-site-scripting-and-how-to-prevent-it/">XSS 攻擊和防堵</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>XSS(Cross Site Scripting) 是一種從網頁的漏洞下手,插入惡意程式碼的攻擊方式。攻擊本身是不給 server 帶來傷害,會造成傷害的是其他使用者。手法是在網站上一些可以讓使用者輸入的地方埋入 html 或是 JavaScript 的惡意腳本,讓其他使用者在瀏覽這個網頁時可以在背後竊取使用者的 cookie 送到指定伺服器或是引導到虛假頁面。</p>
<p>大致分成三種類型:</p>
<h2 id="Stored-XSS-儲存型"><a href="#Stored-XSS-儲存型" class="headerlink" title="Stored XSS (儲存型)"></a>Stored XSS (儲存型)</h2><p>經由使用者輸入,然後被存在 server 資料庫中的 JavaScript,若其後用來作為網頁顯示的時候,沒有過濾或是 encode 處理會被視為正常的 JavaScript 執行,藉此達到攻擊別的使用者的效果。常見的場景如<code>論壇文章、留言板</code>等公開的頁面。</p>
<p>舉個例子,假設有一個 input 可以給使用者留言,如下<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><input type=<span class="string">"text"</span> placeholder=<span class="string">"請留言"</span> /></span><br></pre></td></tr></table></figure></p>
<p>然後攻擊者在 input 輸入下面的內容<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">><script>alert(<span class="string">"XSS攻擊"</span>);</script></span><br></pre></td></tr></table></figure></p>
<p>這樣惡意程式碼會被存在資料庫,之後在瀏覽器解析的時候若沒有經過處理則會被當成一般的 JavaScript 執行,達到攻擊的效果。</p>
<h2 id="Reflected-XSS-反射型"><a href="#Reflected-XSS-反射型" class="headerlink" title="Reflected XSS (反射型)"></a>Reflected XSS (反射型)</h2><p>反射型 xss 不會被儲存在資料庫中,而是 server 收到來自使用者的請求後,未做檢查就將使用者的請求原封不動顯示於回應的網頁當中以達成攻擊效用。可能會出現在 GET 請求中,常見的場景如<code>網頁的搜尋功能</code>,並且只要將帶有惡意程式碼的網址埋在受害者可能會點擊的地方或是分享給受害者點擊,等待受害者上鉤即可。</p>
<p>假設有一個網站有個搜尋功能如下<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://example.com?search=xxx</span><br></pre></td></tr></table></figure></p>
<p>在搜尋完後會顯示結果在網頁上,DOM 可能會長這樣:</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">有關<span class="tag"><<span class="name">span</span> <span class="attr">name</span>=<span class="string">"search"</span>></span>{{$search}}<span class="tag"></<span class="name">span</span>></span>的搜尋結果:</span><br></pre></td></tr></table></figure>
<p>如果我們在搜尋的時候加入如下的惡意程式碼,等到網頁回傳搜尋結果時就達成攻擊<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">http://example.com?search=<script>alert(document.cookie);</script></span><br><span class="line">或</span><br><span class="line">http://example.com?search="><script >alert(document.cookie)</script ></span><br><span class="line">或</span><br><span class="line">http://example.com?search="><ScRiPt>alert(document.cookie)</ScRiPt></span><br><span class="line">或</span><br><span class="line">http://example.com?search="%3cscript%3ealert(document.cookie)%3c/script%3e</span><br></pre></td></tr></table></figure></p>
<h2 id="DOM-Based-XSS"><a href="#DOM-Based-XSS" class="headerlink" title="DOM-Based XSS"></a>DOM-Based XSS</h2><p>藉由 DOM 的漏洞來攻擊,在 DOM 埋入惡意程式碼,藉此來攻擊。</p>
<p>假設網頁上有一段 DOM 如下,<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> send = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> name = <span class="built_in">document</span>.getElementById(<span class="string">'your_name'</span>).value;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">'name'</span>).innerHTML = name;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"name"</span>></span><span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"your_name"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"btn"</span> <span class="attr">onclick</span>=<span class="string">"send();"</span>></span>send<span class="tag"></<span class="name">button</span>></span></span><br></pre></td></tr></table></figure></p>
<p>如果輸入下面的內容,就可以破壞 DOM,執行 JavaScript 達成攻擊了,可以到<a href="https://jsfiddle.net/tracy770830/nyzbfo86/" target="_blank" rel="noopener">這裡</a>測試。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><img src=# onerror="alert('XSS 攻擊');"></span><br></pre></td></tr></table></figure></p>
<p>但被害者不可能自己輸入這種惡意程式碼,除非攻擊者親自到受害者電腦前輸入,否則無法達成攻擊。因此 DOM-Based XSS 通常需要搭配前面兩個手法。讓內容保存在伺服器資料庫中,或是以反射型的方式製造出內容,再藉由 JavaScript 動態產生有效的 DOM 物件來運行惡意程式碼。</p>
<p>根據這三種類型可以整理出以下的表格:</p>
<table>
<thead>
<tr>
<th>惡意程式碼</th>
<th>存放的位置</th>
<th>插入點</th>
</tr>
</thead>
<tbody>
<tr>
<td>儲存型 XSS</td>
<td>後端資料庫</td>
<td>HTML</td>
</tr>
<tr>
<td>反射型 XSS</td>
<td>URL</td>
<td>HTML</td>
</tr>
<tr>
<td>DOM 型 XSS</td>
<td>後端資料庫/前端儲存/URL</td>
<td>前端 JavaScript</td>
</tr>
</tbody>
</table>
<h2 id="防堵方法"><a href="#防堵方法" class="headerlink" title="防堵方法"></a>防堵方法</h2><p>一般來說,防堵的方式不外乎幾種,像是對於使用者傳來的內容做驗證、過濾或是轉譯。驗證的話,有些若是有明確需求的欄位如名字、電話、email 可以使用正則式做驗證,拒絕驗證失敗的內容,此為白名單的作法。而另外一種是只要遇到指定的字元如 <、>、”、’、& 一律過濾掉或是轉譯,但是即便是這樣,攻擊者還是有很多手法可以規避,例如改變字元大小寫、填充額外字元像是空白、使用 URL 編碼、或是使用等價字元等都可以規避驗證。而且可以被惡意注入的地方從 HTML 到 JS 再到 CSS 都有可能 ,所以 XSS 手法變化萬千,如何防堵是個長遠的議題。</p>
<p>個人傾向在輸出時做防堵,不要在存進資料庫時做 encode。<br>原因如下:</p>
<ul>
<li>如果 encode 的方式選錯或是要修改處理會很麻煩</li>
<li>如果有搜尋的需求一樣要根據 encode 的方式處理</li>
<li>根據資料之後可能會被應用的場景不同,可能需要做不同的處理,事先 encode 可能會很麻煩</li>
</ul>
<p>特別說明一下,這邊是指輸入至 database 不過濾或是 encode html 和 JavaScript,但是仍要防堵 SQL injection。</p>
<p>那麼具體來說防堵要怎麼做呢?<br>以下針對不同類型的 XSS 討論一下各別的防堵方法:<br>首先,儲存型和反射型 XSS 都是在前端把惡意程式碼插入到 HTML 上,然後讓瀏覽器執行惡意的 JavaScript 達成攻擊。</p>
<p>防堵的方法有兩種:</p>
<ul>
<li>改成純前端渲染,把程式碼和資料分隔開<br> 一開始先載入一個不含資料的 HTML,資料都是後來由 js append 上 HTML,並且使用屬性明確的函式處理,例如 .innerText、.setAttribute、.style,避免惡意的程式碼被執行。</li>
<li>對 HTML 做充分轉義<br> 如果應用不適合使用純前端渲染的模式,例如有 SEO 需求的考量,需要拼接 HTML,就必須善用模版引擎(template engine) 或是轉譯庫套件</li>
</ul>
<p>而 DOM-Based XSS 則是因為 JavaScript 在執行過程中未對使用者輸入的資料做完善的檢查,導致惡意程式碼被插入 DOM 所產生的攻擊。防堵的方式為儘量減少使用 .innerHTML、.outerHTML、.document.write()等方法,轉而使用<code>.textContent、.setAttribute</code>等。此外,DOM 裡的一些監聽事件如 <code>location、onclick、onerror、onload、onmouseover</code>、以及 <code><a></code> 標籤的 href 屬性都能把字串作為程式碼執行,使用時需要非常小心。</p>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><p>XSS 的重點在於<code>輸出端的防堵</code>,所以<code>謹慎處理從使用者端取得的資料</code>以及<code>過濾敏感字元</code>是可以防堵簡單的 XSS 攻擊,但仍可能被破解。XSS 的攻擊往往道高一尺魔高一丈,善用成熟的模版引擎以及轉譯套件庫也是可以參考的選項。並且時常閱讀 XSS Prevention Cheat Sheet 相關的文件以及注意 OWASP ESAPI(Enterprise Security API)這個專案上的最新攻擊手法,才能儘量降低服務被 XSS 攻擊的風險。</p>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet" target="_blank" rel="noopener">XSS Filter Evasion Cheat Sheet</a></p>
<p><a href="https://www.jishuwen.com/d/2WFh/zh-tw" target="_blank" rel="noopener">前端安全系列(一):如何防止XSS攻擊?</a></p>
<p><a href="https://www.owasp.org/index.php/DOM_Based_XSS" target="_blank" rel="noopener">DOM Based XSS</a></p>
<p><a href="https://www.ithome.com.tw/voice/115685" target="_blank" rel="noopener">XSS與ESAPI</a></p>
<p><a href="https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OTG-INPVAL-001)" target="_blank" rel="noopener">Testing for Reflected Cross site scripting (OTG-INPVAL-001)</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2019/07/03/what-is-cross-site-scripting-and-how-to-prevent-it/" data-id="ckct479de005nn7y5r7tdrjo4" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2019/07/03/what-is-cross-site-scripting-and-how-to-prevent-it/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/security/">security</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/web/">web</a></li></ul>
</footer>
</div>
</article>
<article id="post-cross-origin-resource-sharing" class="article article-type-post" itemscope="" itemprop="blogPost">
<div class="article-meta">
<a href="/2019/06/28/cross-origin-resource-sharing/" class="article-date">
<time datetime="2019-06-27T16:31:23.000Z" itemprop="datePublished">2019-06-28</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/06/28/cross-origin-resource-sharing/">CORS 的介紹與實作</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>在介紹CORS 之前先討論一下什麼是<code>同源政策</code></p>
<h2 id="同源政策-same-origin-policy"><a href="#同源政策-same-origin-policy" class="headerlink" title="同源政策 (same origin policy)"></a>同源政策 (same origin policy)</h2><p>意思就是兩個網頁有相同的<code>domain</code>、<code>protocol</code>以及<code>port</code>號,只要三者有一個不相同即為不同源。</p>
<p>不同源的舉例:</p>
<ul>
<li>從自己的網站向一個第三方 API 發出請求。</li>
<li>從 CDN 載入一些網路資源,例如 CSS、JS、圖片等</li>
</ul>
<p>現代瀏覽器基於安全性考量在你發出非同源的請求時,request 會發出,但是 response 會被瀏覽器擋下來,並且在瀏覽器的 console 裡可以看到錯誤訊息,例如下面這張圖:</p>
<p><img src="https://lh3.googleusercontent.com/OLH4tOE_tnXj9ttrWi-k_N59qOVuGuXhNLYAp-427z_S2qyFGBMz0dJC_tCoQSMCYVPZxAckd7NWklLbfqX7cJa8o3JR1ZReyL6TJw6IItohPM8Oejd_OoRgmD38cATswUMvznBPvW9VC15pILxk32zZsF7o_pdGIUvupR590YGVOranbEbwaOAJPxlTwCOOYoDQ_6SK09J092kJgJ9Lr-eZm03HVbFv8vaWI0sm2VNuLDbvasU6VqCnzGl0lB9tLv7p9ELuP8bkPQ3HzlRygV0LmQci-HxEcpo_ndl9QoNctxk_5gHZrXe0TpYn7fa8p7LXrIRQD1kf3E_TQNywvxd69TDQjUSrNJDA7SFE_-6TTWw3r4Seoe9qEcMhDWf7G_T28a_S33LDiicBug2l71Z7v-c6dPXo0vnLpoUpFDz50O67Iig-U7Q9sPSxPuefI1d-x8Pux5b18TpuHU1ROiNXhD_d8Oh-nEpu7PteKskpmYR_udAAf7KeAd3rJ6sdQjfRumi9lw2Klf0BwuRydPrHRibTdQGhct41WVPnXtQSQO378dHYW2M9LuWblgDTcIUVOUpPfvTaLKXXwiov4YUc8WuATuat9xNqrBTNVQ4rXSnDG8cUYGJTVXgbvc5kpspwHqYe0yEPtAUmu5UJVW5wekpwnKfLbzq9eqfxDDcChmpZUgUzgLJJ=w783-h115-no" alt="same origin policy error"></p>
<p>那要怎麼解呢?<br>只要依循 CORS 就可以了。<br>因為每當發起一個非同源的請求就會受到 <code>CORS</code> 的控制。 </p>
<h2 id="CORS-是什麼"><a href="#CORS-是什麼" class="headerlink" title="CORS 是什麼"></a>CORS 是什麼</h2><p><code>cors (Cross Origin Resource Shraring)</code> 就是 跨來源資源共用。</p>
<p>當你發起一個非同源的請求時, 被請求的 server 必須在 response 的 header 裡加上 <code>Access-Control-Allow-Origin</code> 的設定。<br>當瀏覽器收到 response 後,會查看 response 的 header,如果 header 上的 <code>Access-Control-Allow-Origin</code> 有出現發起請求的 origin ,瀏覽器就會解析 response,結果不會被擋下來。然後 header 大概會長下面這個樣子:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Content-Type: application/json</span><br><span class="line">Content-Length: 250</span><br><span class="line">Connection: keep-alive</span><br><span class="line">Server: nginx</span><br><span class="line">Access-Control-Allow-Origin: *</span><br><span class="line">Cache-Control: no-cache, no-store, must-revalidate, private</span><br><span class="line">Expires: 0</span><br><span class="line">Pragma: no-cache</span><br></pre></td></tr></table></figure>
<p>其中 <code>Access-Control-Allow-Origin: *</code> 表示任何來源都接受。<br>通常除非是公開的 API 服務,不然 server 上都會設定針對 origin 以及 http method 的限制。</p>
<h2 id="Simple-Request-amp-Preflight-Request"><a href="#Simple-Request-amp-Preflight-Request" class="headerlink" title="Simple Request & Preflight Request"></a>Simple Request & Preflight Request</h2><p>其實 CORS 又分為兩種情況,一種是簡單請求(<code>simple request</code>),另一種則是預檢請求(<code>preflight request</code>)。</p>
<p>simple request 的條件需同時滿足以下條件:</p>
<ul>
<li>request method 為 GET、HEAD、POST 其一</li>
<li>Content-Type 為 application/x-www-form-urlencoded、multipart/form-data、text/plain 其一</li>
<li>除了一些開放的 header,沒有指定 custom header</li>
</ul>
<p>詳細的限制可以參考 <a href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS#%E7%B0%A1%E5%96%AE%E8%AB%8B%E6%B1%82" target="_blank" rel="noopener">MDN 簡單請求</a>。</p>
<p>反之,若是使用其他 HTTP method,如 DELETE 或是 PATCH 等,可能需要 server 驗證的情況,這時候會用到 preflight request。<br>假設我們要 DELETE 一筆資源,preflight request 會先發出一個 OPTION 的請求確認 server 是否接受該請求,如果接受才會發出真正的請求,如果不接受的話請求就會在這裡被中斷, DELETE 不會真的被送出。</p>
<h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><ul>
<li>對於非同源的請求,server 還是會接受請求並處理,但是同時 server 也要在 header 設定許可的 <code>Access-Control-Allow-Origin</code> ,瀏覽器才會解析請求結果,否則結果會被瀏覽器所擋下</li>
<li>simple request 只會有一次的請求</li>
<li>preflight request 會先發出一個 OPTION 請求,待 server 許可後 client 才會發出真正的請求,故會有兩次 request 發出</li>
</ul>
<h2 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h2><p><a href="https://blog.techbridge.cc/2017/05/20/api-ajax-cors-and-jsonp/" target="_blank" rel="noopener">輕鬆理解 Ajax 與跨來源請求</a></p>
<p><a href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS" target="_blank" rel="noopener">跨來源資源共用(CORS)</a></p>
<p><a href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Server-Side_Access_Control" target="_blank" rel="noopener">伺服器端存取控制(CORS)</a></p>
<p><a href="https://www.ruanyifeng.com/blog/2016/04/cors.html" target="_blank" rel="noopener">跨域資源共享CORS 詳解</a></p>
<p><a href="https://stackoverflow.com/questions/10636611/how-does-access-control-allow-origin-header-work" target="_blank" rel="noopener">How does Access-Control-Allow-Origin header work?</a></p>
<p><a href="https://w3c.github.io/webappsec-cors-for-developers/#intro" target="_blank" rel="noopener">CORS for Developers</a></p>
</div>
<footer class="article-footer">
<a data-url="https://yuchitung.github.io/2019/06/28/cross-origin-resource-sharing/" data-id="ckct479cr0045n7y5926rid7v" class="article-share-link">Share</a>
<a href="https://yuchitung.github.io/2019/06/28/cross-origin-resource-sharing/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/web/">web</a></li></ul>
</footer>
</div>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="page-number" href="/page/3/">3</a><a class="extend next" rel="next" href="/page/2/">Next »</a>
</nav>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Tags</h3>
<div class="widget">
<ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/PhpStorm/">PhpStorm</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/api/">api</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/db/">db</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/design-pattern/">design-pattern</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/env/">env</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jQuery/">jQuery</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/job/">job</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/kit/">kit</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/laravel/">laravel</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/">mysql</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/oop/">oop</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/php/">php</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/security/">security</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/solid/">solid</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/timezone/">timezone</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ubuntu/">ubuntu</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/web/">web</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Tag Cloud</h3>
<div class="widget tagcloud">
<a href="/tags/PhpStorm/" style="font-size: 10px;">PhpStorm</a> <a href="/tags/api/" style="font-size: 10px;">api</a> <a href="/tags/db/" style="font-size: 10px;">db</a> <a href="/tags/design-pattern/" style="font-size: 10px;">design-pattern</a> <a href="/tags/docker/" style="font-size: 10px;">docker</a> <a href="/tags/env/" style="font-size: 10px;">env</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/jQuery/" style="font-size: 10px;">jQuery</a> <a href="/tags/job/" style="font-size: 10px;">job</a> <a href="/tags/kit/" style="font-size: 10px;">kit</a> <a href="/tags/laravel/" style="font-size: 10px;">laravel</a> <a href="/tags/linux/" style="font-size: 10px;">linux</a> <a href="/tags/mysql/" style="font-size: 13.33px;">mysql</a> <a href="/tags/oop/" style="font-size: 16.67px;">oop</a> <a href="/tags/php/" style="font-size: 13.33px;">php</a> <a href="/tags/security/" style="font-size: 10px;">security</a> <a href="/tags/solid/" style="font-size: 16.67px;">solid</a> <a href="/tags/timezone/" style="font-size: 10px;">timezone</a> <a href="/tags/ubuntu/" style="font-size: 10px;">ubuntu</a> <a href="/tags/web/" style="font-size: 20px;">web</a>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">July 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/04/">April 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">July 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">June 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/04/">April 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/03/">March 2019</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recent Posts</h3>
<div class="widget">
<ul>
<li>
<a href="/2020/07/10/useful-docker-command/">常用 docker 指令</a>
</li>
<li>
<a href="/2020/07/09/useful-mysql-command/">常用的 MySQL 指令</a>
</li>
<li>
<a href="/2020/07/09/my-on-board-kit/">On-board Kit</a>
</li>
<li>
<a href="/2020/07/08/set-up-xdebug-with-phpstorm-and-laradock/">使用 Laradock 和 PhpStorm 設定 xdebug</a>
</li>
<li>
<a href="/2020/07/06/cgi-fastcgi-and-phpfpm/">CGI、FastCGI 和 PHP-FPM</a>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2020 Yuchi Tung<br>