├── .gitignore
├── JavaWeb-Secure-Coding.postman_collection.json
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── java
└── org
│ └── javaweb
│ └── code
│ ├── config
│ ├── JPAConfig.java
│ └── JavaWebCodeApplication.java
│ ├── controller
│ ├── CMDController.java
│ ├── ExpressionController.java
│ ├── SQLInjectionController.java
│ ├── SSRFController.java
│ ├── SSTIController.java
│ ├── XPathController.java
│ └── XXEController.java
│ ├── entity
│ └── SysUser.java
│ ├── mapper
│ └── SysUserMapper.java
│ └── repository
│ ├── SysUserCustomRepository.java
│ ├── SysUserRepository.java
│ └── impl
│ └── SysUserCustomRepositoryImpl.java
└── resources
├── application.properties
├── javaweb-bbs.db
├── mapper
└── SysUser.xml
└── static
└── xxe.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
34 |
35 | .mvn
--------------------------------------------------------------------------------
/JavaWeb-Secure-Coding.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "f2a21800-535d-4f1b-a79d-6606c9d0be80",
4 | "name": "JavaWeb-Secure-Coding",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
6 | "_exporter_id": "21653536"
7 | },
8 | "item": [
9 | {
10 | "name": "SQL",
11 | "item": [
12 | {
13 | "name": "MyBatis",
14 | "item": [
15 | {
16 | "name": "mybatisStringQuery",
17 | "request": {
18 | "method": "GET",
19 | "header": [],
20 | "url": {
21 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisStringQuery.do?username=admin",
22 | "protocol": "http",
23 | "host": [
24 | "{{base_url}}"
25 | ],
26 | "path": [
27 | "SQL",
28 | "Mybatis",
29 | "mybatisStringQuery.do"
30 | ],
31 | "query": [
32 | {
33 | "key": "username",
34 | "value": "admin"
35 | }
36 | ]
37 | }
38 | },
39 | "response": []
40 | },
41 | {
42 | "name": "mybatisStringInjection",
43 | "request": {
44 | "method": "GET",
45 | "header": [],
46 | "url": {
47 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisStringInjection.do?username=admin",
48 | "protocol": "http",
49 | "host": [
50 | "{{base_url}}"
51 | ],
52 | "path": [
53 | "SQL",
54 | "Mybatis",
55 | "mybatisStringInjection.do"
56 | ],
57 | "query": [
58 | {
59 | "key": "username",
60 | "value": "admin"
61 | }
62 | ]
63 | }
64 | },
65 | "response": []
66 | },
67 | {
68 | "name": "mybatisLikeQuery",
69 | "request": {
70 | "method": "GET",
71 | "header": [],
72 | "url": {
73 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisLikeQuery.do?username=a",
74 | "protocol": "http",
75 | "host": [
76 | "{{base_url}}"
77 | ],
78 | "path": [
79 | "SQL",
80 | "Mybatis",
81 | "mybatisLikeQuery.do"
82 | ],
83 | "query": [
84 | {
85 | "key": "username",
86 | "value": "a"
87 | }
88 | ]
89 | }
90 | },
91 | "response": []
92 | },
93 | {
94 | "name": "mybatisLikeInjection",
95 | "request": {
96 | "method": "GET",
97 | "header": [],
98 | "url": {
99 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisLikeInjection.do?username=a",
100 | "protocol": "http",
101 | "host": [
102 | "{{base_url}}"
103 | ],
104 | "path": [
105 | "SQL",
106 | "Mybatis",
107 | "mybatisLikeInjection.do"
108 | ],
109 | "query": [
110 | {
111 | "key": "username",
112 | "value": "a"
113 | }
114 | ]
115 | }
116 | },
117 | "response": []
118 | },
119 | {
120 | "name": "mybatisWhereInQuery",
121 | "request": {
122 | "method": "GET",
123 | "header": [],
124 | "url": {
125 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisWhereInQuery.do?ids=1,2",
126 | "protocol": "http",
127 | "host": [
128 | "{{base_url}}"
129 | ],
130 | "path": [
131 | "SQL",
132 | "Mybatis",
133 | "mybatisWhereInQuery.do"
134 | ],
135 | "query": [
136 | {
137 | "key": "ids",
138 | "value": "1,2"
139 | }
140 | ]
141 | }
142 | },
143 | "response": []
144 | },
145 | {
146 | "name": "mybatisWhereInInjection",
147 | "request": {
148 | "method": "GET",
149 | "header": [],
150 | "url": {
151 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisWhereInInjection.do?ids=1,2",
152 | "protocol": "http",
153 | "host": [
154 | "{{base_url}}"
155 | ],
156 | "path": [
157 | "SQL",
158 | "Mybatis",
159 | "mybatisWhereInInjection.do"
160 | ],
161 | "query": [
162 | {
163 | "key": "ids",
164 | "value": "1,2"
165 | }
166 | ]
167 | }
168 | },
169 | "response": []
170 | },
171 | {
172 | "name": "mybatisOrderByQuery",
173 | "request": {
174 | "method": "GET",
175 | "header": [],
176 | "url": {
177 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisOrderByQuery.do?order=id&orderType=desc",
178 | "protocol": "http",
179 | "host": [
180 | "{{base_url}}"
181 | ],
182 | "path": [
183 | "SQL",
184 | "Mybatis",
185 | "mybatisOrderByQuery.do"
186 | ],
187 | "query": [
188 | {
189 | "key": "order",
190 | "value": "id"
191 | },
192 | {
193 | "key": "orderType",
194 | "value": "desc"
195 | }
196 | ]
197 | }
198 | },
199 | "response": []
200 | },
201 | {
202 | "name": "mybatisOrderByInjection",
203 | "request": {
204 | "method": "GET",
205 | "header": [],
206 | "url": {
207 | "raw": "http://{{base_url}}/SQL/Mybatis/mybatisOrderByInjection.do?order=id&orderType=desc",
208 | "protocol": "http",
209 | "host": [
210 | "{{base_url}}"
211 | ],
212 | "path": [
213 | "SQL",
214 | "Mybatis",
215 | "mybatisOrderByInjection.do"
216 | ],
217 | "query": [
218 | {
219 | "key": "order",
220 | "value": "id"
221 | },
222 | {
223 | "key": "orderType",
224 | "value": "desc"
225 | }
226 | ]
227 | }
228 | },
229 | "response": []
230 | }
231 | ]
232 | },
233 | {
234 | "name": "Spring",
235 | "item": [
236 | {
237 | "name": "jdbcTemplateSQLInjectionTest",
238 | "request": {
239 | "method": "GET",
240 | "header": [],
241 | "url": {
242 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateStringInjection.do?username=admin' and 1=2 union select 1,2,sqlite_version(),4,5,6,'7",
243 | "protocol": "http",
244 | "host": [
245 | "{{base_url}}"
246 | ],
247 | "path": [
248 | "SQL",
249 | "Spring",
250 | "jdbcTemplateStringInjection.do"
251 | ],
252 | "query": [
253 | {
254 | "key": "username",
255 | "value": "admin' and 1=2 union select 1,2,sqlite_version(),4,5,6,'7"
256 | }
257 | ]
258 | }
259 | },
260 | "response": []
261 | },
262 | {
263 | "name": "jdbcTemplateStringInjection",
264 | "request": {
265 | "method": "GET",
266 | "header": [],
267 | "url": {
268 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateStringInjection.do?username=admin",
269 | "protocol": "http",
270 | "host": [
271 | "{{base_url}}"
272 | ],
273 | "path": [
274 | "SQL",
275 | "Spring",
276 | "jdbcTemplateStringInjection.do"
277 | ],
278 | "query": [
279 | {
280 | "key": "username",
281 | "value": "admin"
282 | }
283 | ]
284 | }
285 | },
286 | "response": []
287 | },
288 | {
289 | "name": "jdbcTemplateStringQuery",
290 | "request": {
291 | "method": "GET",
292 | "header": [],
293 | "url": {
294 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateStringQuery.do?username=admin",
295 | "protocol": "http",
296 | "host": [
297 | "{{base_url}}"
298 | ],
299 | "path": [
300 | "SQL",
301 | "Spring",
302 | "jdbcTemplateStringQuery.do"
303 | ],
304 | "query": [
305 | {
306 | "key": "username",
307 | "value": "admin"
308 | }
309 | ]
310 | }
311 | },
312 | "response": []
313 | },
314 | {
315 | "name": "jdbcTemplateLikeQuery",
316 | "request": {
317 | "method": "GET",
318 | "header": [],
319 | "url": {
320 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateLikeQuery.do?username=a",
321 | "protocol": "http",
322 | "host": [
323 | "{{base_url}}"
324 | ],
325 | "path": [
326 | "SQL",
327 | "Spring",
328 | "jdbcTemplateLikeQuery.do"
329 | ],
330 | "query": [
331 | {
332 | "key": "username",
333 | "value": "a"
334 | }
335 | ]
336 | }
337 | },
338 | "response": []
339 | },
340 | {
341 | "name": "jdbcTemplateLikeInjection",
342 | "request": {
343 | "method": "GET",
344 | "header": [],
345 | "url": {
346 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateLikeInjection.do?username=a",
347 | "protocol": "http",
348 | "host": [
349 | "{{base_url}}"
350 | ],
351 | "path": [
352 | "SQL",
353 | "Spring",
354 | "jdbcTemplateLikeInjection.do"
355 | ],
356 | "query": [
357 | {
358 | "key": "username",
359 | "value": "a"
360 | }
361 | ]
362 | }
363 | },
364 | "response": []
365 | },
366 | {
367 | "name": "jdbcTemplateOrderByQuery",
368 | "request": {
369 | "method": "GET",
370 | "header": [],
371 | "url": {
372 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateOrderByQuery.do?order=id&orderType=desc",
373 | "protocol": "http",
374 | "host": [
375 | "{{base_url}}"
376 | ],
377 | "path": [
378 | "SQL",
379 | "Spring",
380 | "jdbcTemplateOrderByQuery.do"
381 | ],
382 | "query": [
383 | {
384 | "key": "order",
385 | "value": "id"
386 | },
387 | {
388 | "key": "orderType",
389 | "value": "desc"
390 | }
391 | ]
392 | }
393 | },
394 | "response": []
395 | },
396 | {
397 | "name": "jdbcTemplateOrderByInjection",
398 | "request": {
399 | "method": "GET",
400 | "header": [],
401 | "url": {
402 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateOrderByInjection.do?order=id&orderType=desc",
403 | "protocol": "http",
404 | "host": [
405 | "{{base_url}}"
406 | ],
407 | "path": [
408 | "SQL",
409 | "Spring",
410 | "jdbcTemplateOrderByInjection.do"
411 | ],
412 | "query": [
413 | {
414 | "key": "order",
415 | "value": "id"
416 | },
417 | {
418 | "key": "orderType",
419 | "value": "desc"
420 | }
421 | ]
422 | }
423 | },
424 | "response": []
425 | },
426 | {
427 | "name": "jdbcTemplateOrderByAppendQuery",
428 | "request": {
429 | "method": "GET",
430 | "header": [],
431 | "url": {
432 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateOrderByAppendQuery.do?order=id&orderType=desc",
433 | "protocol": "http",
434 | "host": [
435 | "{{base_url}}"
436 | ],
437 | "path": [
438 | "SQL",
439 | "Spring",
440 | "jdbcTemplateOrderByAppendQuery.do"
441 | ],
442 | "query": [
443 | {
444 | "key": "order",
445 | "value": "id"
446 | },
447 | {
448 | "key": "orderType",
449 | "value": "desc"
450 | }
451 | ]
452 | }
453 | },
454 | "response": []
455 | },
456 | {
457 | "name": "jdbcTemplateWhereInQuery",
458 | "request": {
459 | "method": "GET",
460 | "header": [],
461 | "url": {
462 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateWhereInQuery.do?ids=1",
463 | "protocol": "http",
464 | "host": [
465 | "{{base_url}}"
466 | ],
467 | "path": [
468 | "SQL",
469 | "Spring",
470 | "jdbcTemplateWhereInQuery.do"
471 | ],
472 | "query": [
473 | {
474 | "key": "ids",
475 | "value": "1"
476 | }
477 | ]
478 | }
479 | },
480 | "response": []
481 | },
482 | {
483 | "name": "jdbcTemplateWhereInInjection",
484 | "request": {
485 | "method": "GET",
486 | "header": [],
487 | "url": {
488 | "raw": "http://{{base_url}}/SQL/Spring/jdbcTemplateWhereInInjection.do?ids=1",
489 | "protocol": "http",
490 | "host": [
491 | "{{base_url}}"
492 | ],
493 | "path": [
494 | "SQL",
495 | "Spring",
496 | "jdbcTemplateWhereInInjection.do"
497 | ],
498 | "query": [
499 | {
500 | "key": "ids",
501 | "value": "1"
502 | }
503 | ]
504 | }
505 | },
506 | "response": []
507 | }
508 | ]
509 | },
510 | {
511 | "name": "JDBC",
512 | "item": [
513 | {
514 | "name": "findById",
515 | "request": {
516 | "method": "GET",
517 | "header": [],
518 | "url": {
519 | "raw": "http://{{base_url}}/JDBC/findById.do?id=1",
520 | "protocol": "http",
521 | "host": [
522 | "{{base_url}}"
523 | ],
524 | "path": [
525 | "JDBC",
526 | "findById.do"
527 | ],
528 | "query": [
529 | {
530 | "key": "id",
531 | "value": "1"
532 | }
533 | ]
534 | }
535 | },
536 | "response": []
537 | },
538 | {
539 | "name": "findSysUserById",
540 | "request": {
541 | "method": "GET",
542 | "header": [],
543 | "url": {
544 | "raw": "http://{{base_url}}/JDBC/findSysUserById.do?id=1",
545 | "protocol": "http",
546 | "host": [
547 | "{{base_url}}"
548 | ],
549 | "path": [
550 | "JDBC",
551 | "findSysUserById.do"
552 | ],
553 | "query": [
554 | {
555 | "key": "id",
556 | "value": "1"
557 | }
558 | ]
559 | }
560 | },
561 | "response": []
562 | }
563 | ]
564 | },
565 | {
566 | "name": "JPA",
567 | "item": [
568 | {
569 | "name": "jpaWhereInQuery",
570 | "request": {
571 | "method": "GET",
572 | "header": [],
573 | "url": {
574 | "raw": "http://{{base_url}}/SQL/JPA/jpaWhereInQuery.do?users=root&users=admin",
575 | "protocol": "http",
576 | "host": [
577 | "{{base_url}}"
578 | ],
579 | "path": [
580 | "SQL",
581 | "JPA",
582 | "jpaWhereInQuery.do"
583 | ],
584 | "query": [
585 | {
586 | "key": "users",
587 | "value": "root"
588 | },
589 | {
590 | "key": "users",
591 | "value": "admin"
592 | }
593 | ]
594 | }
595 | },
596 | "response": []
597 | },
598 | {
599 | "name": "jpaLikeQuery",
600 | "request": {
601 | "method": "GET",
602 | "header": [],
603 | "url": {
604 | "raw": "http://{{base_url}}/SQL/JPA/jpaLikeQuery.do?username=root",
605 | "protocol": "http",
606 | "host": [
607 | "{{base_url}}"
608 | ],
609 | "path": [
610 | "SQL",
611 | "JPA",
612 | "jpaLikeQuery.do"
613 | ],
614 | "query": [
615 | {
616 | "key": "username",
617 | "value": "root"
618 | }
619 | ]
620 | }
621 | },
622 | "response": []
623 | },
624 | {
625 | "name": "jpaLikeAndOrderByQuery",
626 | "request": {
627 | "method": "GET",
628 | "header": [],
629 | "url": {
630 | "raw": "http://{{base_url}}/SQL/JPA/jpaLikeAndOrderByQuery.do?username=a",
631 | "protocol": "http",
632 | "host": [
633 | "{{base_url}}"
634 | ],
635 | "path": [
636 | "SQL",
637 | "JPA",
638 | "jpaLikeAndOrderByQuery.do"
639 | ],
640 | "query": [
641 | {
642 | "key": "username",
643 | "value": "a"
644 | }
645 | ]
646 | }
647 | },
648 | "response": []
649 | },
650 | {
651 | "name": "jpaUsernameBindTest",
652 | "request": {
653 | "method": "GET",
654 | "header": [],
655 | "url": {
656 | "raw": "http://{{base_url}}/SQL/JPA/jpaUsernameBindTest.do?username=admin",
657 | "protocol": "http",
658 | "host": [
659 | "{{base_url}}"
660 | ],
661 | "path": [
662 | "SQL",
663 | "JPA",
664 | "jpaUsernameBindTest.do"
665 | ],
666 | "query": [
667 | {
668 | "key": "username",
669 | "value": "admin"
670 | }
671 | ]
672 | }
673 | },
674 | "response": []
675 | },
676 | {
677 | "name": "jpaEmailBindTest",
678 | "request": {
679 | "method": "GET",
680 | "header": [],
681 | "url": {
682 | "raw": "http://{{base_url}}/SQL/JPA/jpaEmailBindTest.do?username=admin@baidu.com",
683 | "protocol": "http",
684 | "host": [
685 | "{{base_url}}"
686 | ],
687 | "path": [
688 | "SQL",
689 | "JPA",
690 | "jpaEmailBindTest.do"
691 | ],
692 | "query": [
693 | {
694 | "key": "username",
695 | "value": "admin@baidu.com"
696 | }
697 | ]
698 | }
699 | },
700 | "response": []
701 | },
702 | {
703 | "name": "jpaIdBindTest",
704 | "request": {
705 | "method": "GET",
706 | "header": [],
707 | "url": {
708 | "raw": "http://{{base_url}}/SQL/JPA/jpaIdBindTest.do?id=1",
709 | "protocol": "http",
710 | "host": [
711 | "{{base_url}}"
712 | ],
713 | "path": [
714 | "SQL",
715 | "JPA",
716 | "jpaIdBindTest.do"
717 | ],
718 | "query": [
719 | {
720 | "key": "id",
721 | "value": "1"
722 | }
723 | ]
724 | }
725 | },
726 | "response": []
727 | },
728 | {
729 | "name": "jpqlQuery",
730 | "request": {
731 | "method": "GET",
732 | "header": [],
733 | "url": {
734 | "raw": "http://{{base_url}}/SQL/JPA/jpqlQuery.do?username=admin",
735 | "protocol": "http",
736 | "host": [
737 | "{{base_url}}"
738 | ],
739 | "path": [
740 | "SQL",
741 | "JPA",
742 | "jpqlQuery.do"
743 | ],
744 | "query": [
745 | {
746 | "key": "username",
747 | "value": "admin"
748 | }
749 | ]
750 | }
751 | },
752 | "response": []
753 | },
754 | {
755 | "name": "jpqlInjection",
756 | "request": {
757 | "method": "GET",
758 | "header": [],
759 | "url": {
760 | "raw": "http://{{base_url}}/SQL/JPA/jpqlInjection.do?username=admin",
761 | "protocol": "http",
762 | "host": [
763 | "{{base_url}}"
764 | ],
765 | "path": [
766 | "SQL",
767 | "JPA",
768 | "jpqlInjection.do"
769 | ],
770 | "query": [
771 | {
772 | "key": "username",
773 | "value": "admin"
774 | }
775 | ]
776 | }
777 | },
778 | "response": []
779 | },
780 | {
781 | "name": "nativeQuery",
782 | "request": {
783 | "method": "GET",
784 | "header": [],
785 | "url": {
786 | "raw": "http://{{base_url}}/SQL/JPA/nativeQuery.do?username=admin",
787 | "protocol": "http",
788 | "host": [
789 | "{{base_url}}"
790 | ],
791 | "path": [
792 | "SQL",
793 | "JPA",
794 | "nativeQuery.do"
795 | ],
796 | "query": [
797 | {
798 | "key": "username",
799 | "value": "admin"
800 | }
801 | ]
802 | }
803 | },
804 | "response": []
805 | },
806 | {
807 | "name": "nativeInjection",
808 | "request": {
809 | "method": "GET",
810 | "header": [],
811 | "url": {
812 | "raw": "http://{{base_url}}/SQL/JPA/nativeInjection.do?username=admin",
813 | "protocol": "http",
814 | "host": [
815 | "{{base_url}}"
816 | ],
817 | "path": [
818 | "SQL",
819 | "JPA",
820 | "nativeInjection.do"
821 | ],
822 | "query": [
823 | {
824 | "key": "username",
825 | "value": "admin"
826 | }
827 | ]
828 | }
829 | },
830 | "response": []
831 | },
832 | {
833 | "name": "namedQuery",
834 | "request": {
835 | "method": "GET",
836 | "header": [],
837 | "url": {
838 | "raw": "http://{{base_url}}/SQL/JPA/namedQuery.do?username=admin",
839 | "protocol": "http",
840 | "host": [
841 | "{{base_url}}"
842 | ],
843 | "path": [
844 | "SQL",
845 | "JPA",
846 | "namedQuery.do"
847 | ],
848 | "query": [
849 | {
850 | "key": "username",
851 | "value": "admin"
852 | }
853 | ]
854 | }
855 | },
856 | "response": []
857 | },
858 | {
859 | "name": "criteriaQuery",
860 | "request": {
861 | "method": "GET",
862 | "header": [],
863 | "url": {
864 | "raw": "http://{{base_url}}/SQL/JPA/criteriaQuery.do?email=admin@baidu.com",
865 | "protocol": "http",
866 | "host": [
867 | "{{base_url}}"
868 | ],
869 | "path": [
870 | "SQL",
871 | "JPA",
872 | "criteriaQuery.do"
873 | ],
874 | "query": [
875 | {
876 | "key": "email",
877 | "value": "admin@baidu.com"
878 | }
879 | ]
880 | }
881 | },
882 | "response": []
883 | }
884 | ]
885 | }
886 | ]
887 | },
888 | {
889 | "name": "SSRF",
890 | "item": [
891 | {
892 | "name": "urlConnection",
893 | "request": {
894 | "method": "GET",
895 | "header": [],
896 | "url": {
897 | "raw": "http://{{base_url}}/SSRF/urlConnection.do?url=https://www.baidu.com",
898 | "protocol": "http",
899 | "host": [
900 | "{{base_url}}"
901 | ],
902 | "path": [
903 | "SSRF",
904 | "urlConnection.do"
905 | ],
906 | "query": [
907 | {
908 | "key": "url",
909 | "value": "https://www.baidu.com"
910 | }
911 | ]
912 | }
913 | },
914 | "response": []
915 | },
916 | {
917 | "name": "urlFilterConnection",
918 | "request": {
919 | "method": "GET",
920 | "header": [],
921 | "url": {
922 | "raw": "http://{{base_url}}/SSRF/urlFilterConnection.do?url=https://www.baidu.com",
923 | "protocol": "http",
924 | "host": [
925 | "{{base_url}}"
926 | ],
927 | "path": [
928 | "SSRF",
929 | "urlFilterConnection.do"
930 | ],
931 | "query": [
932 | {
933 | "key": "url",
934 | "value": "https://www.baidu.com"
935 | }
936 | ]
937 | }
938 | },
939 | "response": []
940 | }
941 | ]
942 | },
943 | {
944 | "name": "XXE",
945 | "item": [
946 | {
947 | "name": "dom4jSAXReaderXXE",
948 | "request": {
949 | "method": "POST",
950 | "header": [],
951 | "body": {
952 | "mode": "raw",
953 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
954 | "options": {
955 | "raw": {
956 | "language": "xml"
957 | }
958 | }
959 | },
960 | "url": {
961 | "raw": "http://{{base_url}}/XXE/dom4jSAXReaderXXE.do",
962 | "protocol": "http",
963 | "host": [
964 | "{{base_url}}"
965 | ],
966 | "path": [
967 | "XXE",
968 | "dom4jSAXReaderXXE.do"
969 | ]
970 | }
971 | },
972 | "response": []
973 | },
974 | {
975 | "name": "dom4jSAXReader",
976 | "request": {
977 | "method": "POST",
978 | "header": [],
979 | "body": {
980 | "mode": "raw",
981 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
982 | "options": {
983 | "raw": {
984 | "language": "xml"
985 | }
986 | }
987 | },
988 | "url": {
989 | "raw": "http://{{base_url}}/XXE/dom4jSAXReader.do",
990 | "protocol": "http",
991 | "host": [
992 | "{{base_url}}"
993 | ],
994 | "path": [
995 | "XXE",
996 | "dom4jSAXReader.do"
997 | ]
998 | }
999 | },
1000 | "response": []
1001 | },
1002 | {
1003 | "name": "jaxpSAXParserFactoryXXE",
1004 | "request": {
1005 | "method": "POST",
1006 | "header": [],
1007 | "body": {
1008 | "mode": "raw",
1009 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1010 | "options": {
1011 | "raw": {
1012 | "language": "xml"
1013 | }
1014 | }
1015 | },
1016 | "url": {
1017 | "raw": "http://{{base_url}}/XXE/jaxpSAXParserFactoryXXE.do",
1018 | "protocol": "http",
1019 | "host": [
1020 | "{{base_url}}"
1021 | ],
1022 | "path": [
1023 | "XXE",
1024 | "jaxpSAXParserFactoryXXE.do"
1025 | ]
1026 | }
1027 | },
1028 | "response": []
1029 | },
1030 | {
1031 | "name": "jaxpSAXParserFactory",
1032 | "request": {
1033 | "method": "POST",
1034 | "header": [],
1035 | "body": {
1036 | "mode": "raw",
1037 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1038 | "options": {
1039 | "raw": {
1040 | "language": "xml"
1041 | }
1042 | }
1043 | },
1044 | "url": {
1045 | "raw": "http://{{base_url}}/XXE/jaxpSAXParserFactory.do",
1046 | "protocol": "http",
1047 | "host": [
1048 | "{{base_url}}"
1049 | ],
1050 | "path": [
1051 | "XXE",
1052 | "jaxpSAXParserFactory.do"
1053 | ]
1054 | }
1055 | },
1056 | "response": []
1057 | },
1058 | {
1059 | "name": "saxBuilderXXE",
1060 | "request": {
1061 | "method": "POST",
1062 | "header": [],
1063 | "body": {
1064 | "mode": "raw",
1065 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1066 | "options": {
1067 | "raw": {
1068 | "language": "xml"
1069 | }
1070 | }
1071 | },
1072 | "url": {
1073 | "raw": "http://{{base_url}}/XXE/saxBuilderXXE.do",
1074 | "protocol": "http",
1075 | "host": [
1076 | "{{base_url}}"
1077 | ],
1078 | "path": [
1079 | "XXE",
1080 | "saxBuilderXXE.do"
1081 | ]
1082 | }
1083 | },
1084 | "response": []
1085 | },
1086 | {
1087 | "name": "saxBuilder",
1088 | "request": {
1089 | "method": "POST",
1090 | "header": [],
1091 | "body": {
1092 | "mode": "raw",
1093 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1094 | "options": {
1095 | "raw": {
1096 | "language": "xml"
1097 | }
1098 | }
1099 | },
1100 | "url": {
1101 | "raw": "http://{{base_url}}/XXE/saxBuilder.do",
1102 | "protocol": "http",
1103 | "host": [
1104 | "{{base_url}}"
1105 | ],
1106 | "path": [
1107 | "XXE",
1108 | "saxBuilder.do"
1109 | ]
1110 | }
1111 | },
1112 | "response": []
1113 | },
1114 | {
1115 | "name": "xmlReaderXXE",
1116 | "request": {
1117 | "method": "POST",
1118 | "header": [],
1119 | "body": {
1120 | "mode": "raw",
1121 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1122 | "options": {
1123 | "raw": {
1124 | "language": "xml"
1125 | }
1126 | }
1127 | },
1128 | "url": {
1129 | "raw": "http://{{base_url}}/XXE/xmlReaderXXE.do",
1130 | "protocol": "http",
1131 | "host": [
1132 | "{{base_url}}"
1133 | ],
1134 | "path": [
1135 | "XXE",
1136 | "xmlReaderXXE.do"
1137 | ]
1138 | }
1139 | },
1140 | "response": []
1141 | },
1142 | {
1143 | "name": "documentBuilderFactoryXXE",
1144 | "request": {
1145 | "method": "POST",
1146 | "header": [],
1147 | "body": {
1148 | "mode": "raw",
1149 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1150 | "options": {
1151 | "raw": {
1152 | "language": "xml"
1153 | }
1154 | }
1155 | },
1156 | "url": {
1157 | "raw": "http://{{base_url}}/XXE/documentBuilderFactoryXXE.do",
1158 | "protocol": "http",
1159 | "host": [
1160 | "{{base_url}}"
1161 | ],
1162 | "path": [
1163 | "XXE",
1164 | "documentBuilderFactoryXXE.do"
1165 | ]
1166 | }
1167 | },
1168 | "response": []
1169 | },
1170 | {
1171 | "name": "documentBuilderFactory",
1172 | "request": {
1173 | "method": "POST",
1174 | "header": [],
1175 | "body": {
1176 | "mode": "raw",
1177 | "raw": "\r\n \r\n \r\n \r\n \r\n]>\r\n\r\n &content;&end;\r\n",
1178 | "options": {
1179 | "raw": {
1180 | "language": "xml"
1181 | }
1182 | }
1183 | },
1184 | "url": {
1185 | "raw": "http://{{base_url}}/XXE/documentBuilderFactory.do",
1186 | "protocol": "http",
1187 | "host": [
1188 | "{{base_url}}"
1189 | ],
1190 | "path": [
1191 | "XXE",
1192 | "documentBuilderFactory.do"
1193 | ]
1194 | }
1195 | },
1196 | "response": []
1197 | },
1198 | {
1199 | "name": "jaxbContext",
1200 | "request": {
1201 | "method": "POST",
1202 | "header": [],
1203 | "body": {
1204 | "mode": "raw",
1205 | "raw": "\r\n\r\n 1\r\n",
1206 | "options": {
1207 | "raw": {
1208 | "language": "xml"
1209 | }
1210 | }
1211 | },
1212 | "url": {
1213 | "raw": "http://{{base_url}}/XXE/jaxbContext.do",
1214 | "protocol": "http",
1215 | "host": [
1216 | "{{base_url}}"
1217 | ],
1218 | "path": [
1219 | "XXE",
1220 | "jaxbContext.do"
1221 | ]
1222 | }
1223 | },
1224 | "response": []
1225 | }
1226 | ]
1227 | },
1228 | {
1229 | "name": "XPath",
1230 | "item": [
1231 | {
1232 | "name": "xpathInjection",
1233 | "request": {
1234 | "method": "GET",
1235 | "header": [],
1236 | "url": {
1237 | "raw": "http://{{base_url}}/XPath/xpathInjection.do?username=admin' or '",
1238 | "protocol": "http",
1239 | "host": [
1240 | "{{base_url}}"
1241 | ],
1242 | "path": [
1243 | "XPath",
1244 | "xpathInjection.do"
1245 | ],
1246 | "query": [
1247 | {
1248 | "key": "username",
1249 | "value": "admin' or '"
1250 | }
1251 | ]
1252 | }
1253 | },
1254 | "response": []
1255 | },
1256 | {
1257 | "name": "xpathQuery",
1258 | "request": {
1259 | "method": "GET",
1260 | "header": [],
1261 | "url": {
1262 | "raw": "http://{{base_url}}/XPath/xpathQuery.do?username=admin&password=admin123",
1263 | "protocol": "http",
1264 | "host": [
1265 | "{{base_url}}"
1266 | ],
1267 | "path": [
1268 | "XPath",
1269 | "xpathQuery.do"
1270 | ],
1271 | "query": [
1272 | {
1273 | "key": "username",
1274 | "value": "admin"
1275 | },
1276 | {
1277 | "key": "password",
1278 | "value": "admin123"
1279 | }
1280 | ]
1281 | }
1282 | },
1283 | "response": []
1284 | }
1285 | ]
1286 | },
1287 | {
1288 | "name": "Expression",
1289 | "item": [
1290 | {
1291 | "name": "Ognl",
1292 | "request": {
1293 | "method": "GET",
1294 | "header": [],
1295 | "url": {
1296 | "raw": "http://{{base_url}}/Expression/ognl.do?exp=@java.lang.Runtime@getRuntime().exec('calc')",
1297 | "protocol": "http",
1298 | "host": [
1299 | "{{base_url}}"
1300 | ],
1301 | "path": [
1302 | "Expression",
1303 | "ognl.do"
1304 | ],
1305 | "query": [
1306 | {
1307 | "key": "exp",
1308 | "value": "@java.lang.Runtime@getRuntime().exec('calc')"
1309 | }
1310 | ]
1311 | }
1312 | },
1313 | "response": []
1314 | },
1315 | {
1316 | "name": "SpEL",
1317 | "request": {
1318 | "method": "GET",
1319 | "header": [],
1320 | "url": {
1321 | "raw": "http://{{base_url}}/Expression/spEL.do?exp=new java.lang.ProcessBuilder('calc').start()",
1322 | "protocol": "http",
1323 | "host": [
1324 | "{{base_url}}"
1325 | ],
1326 | "path": [
1327 | "Expression",
1328 | "spEL.do"
1329 | ],
1330 | "query": [
1331 | {
1332 | "key": "exp",
1333 | "value": "new java.lang.ProcessBuilder('calc').start()"
1334 | }
1335 | ]
1336 | }
1337 | },
1338 | "response": []
1339 | },
1340 | {
1341 | "name": "MVEL2",
1342 | "request": {
1343 | "method": "GET",
1344 | "header": [],
1345 | "url": {
1346 | "raw": "http://{{base_url}}/Expression/mvel.do?exp=new java.lang.String(new java.lang.ProcessBuilder('whoami').start().getInputStream().readAllBytes())",
1347 | "protocol": "http",
1348 | "host": [
1349 | "{{base_url}}"
1350 | ],
1351 | "path": [
1352 | "Expression",
1353 | "mvel.do"
1354 | ],
1355 | "query": [
1356 | {
1357 | "key": "exp",
1358 | "value": "new java.lang.String(new java.lang.ProcessBuilder('whoami').start().getInputStream().readAllBytes())"
1359 | }
1360 | ]
1361 | }
1362 | },
1363 | "response": []
1364 | },
1365 | {
1366 | "name": "ScriptEngine",
1367 | "request": {
1368 | "method": "GET",
1369 | "header": [],
1370 | "url": {
1371 | "raw": "http://{{base_url}}/Expression/scriptEngine.do?exp=new java.lang.String(new java.lang.ProcessBuilder('whoami').start().getInputStream().readAllBytes())",
1372 | "protocol": "http",
1373 | "host": [
1374 | "{{base_url}}"
1375 | ],
1376 | "path": [
1377 | "Expression",
1378 | "scriptEngine.do"
1379 | ],
1380 | "query": [
1381 | {
1382 | "key": "exp",
1383 | "value": "new java.lang.String(new java.lang.ProcessBuilder('whoami').start().getInputStream().readAllBytes())"
1384 | }
1385 | ]
1386 | }
1387 | },
1388 | "response": []
1389 | }
1390 | ]
1391 | },
1392 | {
1393 | "name": "SSTI",
1394 | "item": [
1395 | {
1396 | "name": "Velocity",
1397 | "request": {
1398 | "method": "GET",
1399 | "header": [],
1400 | "url": {
1401 | "raw": "http://{{base_url}}/SSTI/velocity.do?tpl=%23set($e='e')%23set($c=$e.getClass().forName('org.apache.commons.io.IOUtils'))$c.getMethod('toString',$e.getClass().forName('java.io.InputStream')).invoke(null, $e.getClass().forName('java.lang.Runtime').getMethod('exec', $e.getClass().forName('java.lang.String')).invoke($e.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'whoami').getInputStream())",
1402 | "protocol": "http",
1403 | "host": [
1404 | "{{base_url}}"
1405 | ],
1406 | "path": [
1407 | "SSTI",
1408 | "velocity.do"
1409 | ],
1410 | "query": [
1411 | {
1412 | "key": "tpl",
1413 | "value": "%23set($e='e')%23set($c=$e.getClass().forName('org.apache.commons.io.IOUtils'))$c.getMethod('toString',$e.getClass().forName('java.io.InputStream')).invoke(null, $e.getClass().forName('java.lang.Runtime').getMethod('exec', $e.getClass().forName('java.lang.String')).invoke($e.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null),'whoami').getInputStream())"
1414 | }
1415 | ]
1416 | }
1417 | },
1418 | "response": []
1419 | },
1420 | {
1421 | "name": "Freemarker",
1422 | "request": {
1423 | "method": "GET",
1424 | "header": [],
1425 | "url": {
1426 | "raw": "http://{{base_url}}/SSTI/freemarker.do?tpl=<%23assign ob='freemarker.template.utility.ObjectConstructor'?new()><%23assign value='freemarker.template.utility.Execute'?new()>$%7Bvalue('calc')%7D",
1427 | "protocol": "http",
1428 | "host": [
1429 | "{{base_url}}"
1430 | ],
1431 | "path": [
1432 | "SSTI",
1433 | "freemarker.do"
1434 | ],
1435 | "query": [
1436 | {
1437 | "key": "tpl",
1438 | "value": "<%23assign ob='freemarker.template.utility.ObjectConstructor'?new()><%23assign value='freemarker.template.utility.Execute'?new()>$%7Bvalue('calc')%7D"
1439 | }
1440 | ]
1441 | }
1442 | },
1443 | "response": []
1444 | }
1445 | ]
1446 | },
1447 | {
1448 | "name": "CMD",
1449 | "item": [
1450 | {
1451 | "name": "Ping",
1452 | "request": {
1453 | "method": "GET",
1454 | "header": [],
1455 | "url": {
1456 | "raw": "http://{{base_url}}/CMD/ping.do?host=baidu.com",
1457 | "protocol": "http",
1458 | "host": [
1459 | "{{base_url}}"
1460 | ],
1461 | "path": [
1462 | "CMD",
1463 | "ping.do"
1464 | ],
1465 | "query": [
1466 | {
1467 | "key": "host",
1468 | "value": "baidu.com"
1469 | }
1470 | ]
1471 | }
1472 | },
1473 | "response": []
1474 | },
1475 | {
1476 | "name": "PingRCE",
1477 | "request": {
1478 | "method": "GET",
1479 | "header": [],
1480 | "url": {
1481 | "raw": "http://{{base_url}}/CMD/pingRCE.do?host=baidu.com %26%26 whoami",
1482 | "protocol": "http",
1483 | "host": [
1484 | "{{base_url}}"
1485 | ],
1486 | "path": [
1487 | "CMD",
1488 | "pingRCE.do"
1489 | ],
1490 | "query": [
1491 | {
1492 | "key": "host",
1493 | "value": "baidu.com %26%26 whoami"
1494 | }
1495 | ]
1496 | }
1497 | },
1498 | "response": []
1499 | }
1500 | ]
1501 | }
1502 | ],
1503 | "event": [
1504 | {
1505 | "listen": "prerequest",
1506 | "script": {
1507 | "type": "text/javascript",
1508 | "exec": [
1509 | ""
1510 | ]
1511 | }
1512 | },
1513 | {
1514 | "listen": "test",
1515 | "script": {
1516 | "type": "text/javascript",
1517 | "exec": [
1518 | ""
1519 | ]
1520 | }
1521 | }
1522 | ],
1523 | "variable": [
1524 | {
1525 | "key": "base_url",
1526 | "value": "localhost:8080",
1527 | "type": "string"
1528 | }
1529 | ]
1530 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java安全开发规范
2 |
3 | 随着数字化时代的到来,我们的生活日益依赖Web服务和Web应用,它们为我们提供了便捷的办公、交易和生活方式。然而,Web服务在为人们提供着更加便捷的生活方式的同时也面临着网络安全风险的巨大威胁,Web应用成为了黑客攻击的首要目标。
4 |
5 | 根据中国国家互联网应急中心(CNCERT/CC)和国际权威安全监测机构Gartner的数据,75%以上的攻击针对Web应用,而约2/3的Web站点存在着相当严重的安全漏洞。这些数据清晰地展示了网络安全问题的紧迫性和严重性。
6 |
7 | 为了保护数据免受窃取、网站免受篡改等安全威胁,程序开发者在开发便捷、强大的Web应用的同时,还要确保程序自身的安全性。其中尤为重要的是开发者需密切关注Web安全、遵循安全编码规范。
8 |
9 | Github:[javaweb-secure-coding](https://github.com/javaweb-rasp/javaweb-secure-coding)
10 |
11 | PostMan:[JavaWeb-Secure-Coding.postman_collection.json](https://github.com/javaweb-rasp/javaweb-secure-coding/blob/master/JavaWeb-Secure-Coding.postman_collection.json)
12 |
13 |
14 |
15 | ## 1. 防御性编程
16 |
17 | 防御性编程(Defensive Programming)是一种软件开发方法,旨在最大程度地减少软件缺陷、漏洞和安全风险的产生。防御性编程的核心思想是通过编码和设计技巧来防止和减轻错误和异常,编写代码时要进行输入验证、数据验证和错误处理,以减少漏洞的可能性。
18 |
19 | 防御性编程要求开发者应当始终假定攻击者会尝试利用漏洞来攻击应用程序,也不能因为网络隔离、登录验证登限制而忽视安全,因为攻击者通常会使用非常规手段入侵到内部系统。
20 |
21 |
22 |
23 | ### 1.1 输入验证
24 |
25 | 一切来源于外部请求的参数都可能是恶意的,服务端应当谨慎处理所有来自于请求中的数据,包括但不限于:
26 |
27 | 1. HTTP的请求消息体(表单请求、JSON、XML等);
28 | 2. HTTP的请求参数(GET/POST/DELETE/UPDATE等);
29 | 3. HTTP请求的URL(URL传参,如:RESTful、Matrix请求);
30 | 4. HTTP请求中的Header信息(Cookie、X-Forwarded-For、Referer等);
31 | 5. 文件上传请求中的文件名、文件内容、表单域;
32 | 6. RPC/RESTful请求中的JSON/XML、反序列化对象等;
33 | 7. 客户端传入的加密消息(如:Android、JavaScript加密算法可逆或可构造参数);
34 | 8. 不可信的外部资源文件(如:HTML加载了不安全的外部JS或图片);
35 |
36 |
37 |
38 | ### 1.2 最小权限原则
39 |
40 | 最小权限原则(Principle of Least Privilege,简称PoLP)是计算机安全和信息安全领域的一项核心原则。该原则强调只为执行任务所需的最低权限赋予用户、程序或系统组件。这有助于减少潜在的风险和攻击面,提高系统的安全性。以下是最小权限原则的关键概念和原则:
41 |
42 | 1. **最小权限原则核心观点:** 用户、程序或系统组件应该仅在执行其任务所需的最低权限下运行。这意味着不应授予超出必需权限的额外权限。
43 | 2. **权限分类:** 最小权限原则涉及到对不同类型的权限进行分类。通常包括读取权限、写入权限、执行权限和管理权限等。
44 | 3. **降低攻击面:** 通过限制权限,可以减少潜在攻击者利用的机会。攻击者可能会试图滥用赋予的权限来入侵系统或获取敏感信息。
45 | 4. **最小化数据访问:** 在数据库和应用程序中,最小权限原则还包括限制对数据的访问。用户只应能够访问其需要的数据,而不是整个数据库。
46 | 5. **安全角色和分隔:** 为不同的任务和角色创建安全角色,并授予这些角色所需的最低权限。这有助于将权限管理简化为角色管理。
47 | 6. **特权分离:** 将系统的特权分离,确保只有经过授权的管理员才能执行敏感操作,如系统配置、用户管理等。
48 | 7. **授权控制:** 使用访问控制机制(如访问控制列表(ACL)或基于角色的访问控制(RBAC))来管理和强制权限。
49 | 8. **日志和监控:** 监控和记录权限的使用,以便检测异常行为和未经授权的访问尝试。
50 | 9. **敏感数据保护:** 特别关注对敏感数据的权限控制。确保只有经过授权的用户能够访问和处理敏感数据。
51 | 10. **持续评估:** 定期评估和审查权限,以确保其仍然符合实际需求,并根据需要进行调整。
52 |
53 | 最小权限原则是一项关键的安全实践,有助于减轻内部和外部威胁,并提高系统的安全性。它应该在设计和实施系统、应用程序和网络时得到广泛应用,以确保敏感信息和资源受到适当的保护。同时,最小权限原则也有助于降低人为错误和不当操作的风险,提高系统的可靠性和可维护性。
54 |
55 |
56 |
57 | ### 1.3 单元测试规范
58 |
59 | 单元测试规范是确保代码质量和可维护性的关键组成部分,因此强烈建议启用单元测试,以下是部分单元测试的开发规范:
60 |
61 | 1. 测试用例(Test Case)和测试类(Test Class)应当使用明确的、描述性的名称,以便于理解测试的目的。可以使用约定俗成的命名方式,如在被测试的类名后加上"Test";
62 | 2. 单一职责原则,每个测试用例应该专注于测试一个特定的行为、方法或函数。不要在一个测试用例中尝试覆盖太多功能;
63 | 3. 代码覆盖率测试,确保测试覆盖代码的不同路径和分支,以尽可能提高代码覆盖率;
64 | 4. 安全测试,某些可能存在安全风险的业务需编写安全测试用例,以确保代码没有潜在的安全漏洞;
65 | 5. 使用断言(Assertions)来检查预期结果和实际结果是否匹配,断言应该是清晰和有意义的,以便在测试失败时快速定位问题;
66 | 6. 性能测试,对于一些有性能要求的业务应当编写性能测试用例,以确保代码在高负载情况下仍然具有良好的性能;
67 |
68 |
69 |
70 | ### 1.4 异常处理规范
71 |
72 | 1. 使用自定义的错误页面,在Web应用程序中,使用自定义错误页面来代替默认的错误消息,从而减少信息泄露风险,同时提供更友好的用户体验;
73 | 2. 使用全局异常处理方案,禁止直接将服务端的异常直接输出到客户端;
74 | 3. 使用合适的日志工具记录异常信息,如:Log4j、Logback等,需要特别注意不要使用存在漏洞的Log4j版本(<=2.14.1);
75 | 4. 使用准确的异常类型,以便能够更精确地识别问题和处理异常;
76 | 5. 准确描述自定义类异常信息,在抛出和捕获异常时,必须提供有意义的异常消息,以便在日志中记录或在错误页面上显示;
77 | 6. 合理记录异常信息,以便在出现问题时能够进行故障排除;
78 | 7. 明确的异常声明,在方法签名和JavaDoc中清晰地说明可能抛出的异常,以便他人能够正确地处理;
79 | 8. 禁止滥用异常机制,避免在正常业务控制流中使用异常来进行流程控制。异常应该用于处理异常情况,而不是预期的业务逻辑;
80 | 9. 合理的忽略异常,不要随意的忽略异常(例如,使用空的catch块),这会导致问题被忽略或难以调试;
81 | 10. 合理的异常日志归类,按照业务或异常类型单独记录异常信息;
82 | 11. 在处理用户输入时,进行恶意输入检查,以防止攻击,如:跨站脚本或SQL注入;
83 |
84 |
85 |
86 | ### 1.5 日志存储规范
87 |
88 | 按照信息安全等级保护(等保)的要求,日志存储应遵循一系列规范和最佳实践,以确保敏感信息的保密性、完整性和可用性。以下是针对日志存储的等保部分要求:
89 |
90 | 1. **保密性(C):**
91 | - 加密:对存储的日志数据进行加密,以防止未经授权的访问。
92 | - 访问控制:实施适当的访问控制措施,只有授权用户能够访问和查看日志文件。
93 | - 脱敏:对于包含敏感信息的日志,进行脱敏处理,以隐藏真实的敏感信息。
94 | - 安全传输:确保在日志数据从源传输到存储位置的过程中也是加密的,以避免窃听。
95 | 2. **完整性(I):**
96 | - 整体性保护:使用哈希值或数字签名等技术来验证日志文件的完整性,确保文件在存储过程中未被篡改。
97 | - 访问审计:记录和监控对日志存储的访问,包括读取、写入和修改操作。
98 | 3. **可用性(A):**
99 | - 容灾备份:实施容灾备份策略,确保即使在系统故障或灾难发生时,日志数据仍然可用。
100 | - 定期备份:定期备份日志数据,以防止数据丢失,并确保数据可以恢复。
101 | - 存储容量管理:确保足够的存储容量,以满足日志数据的增长需求,防止因存储不足而丢失日志。
102 | 4. **可追溯性(T):**
103 | - 记录详细信息:在日志中记录详细的事件信息,包括时间戳、事件类型、事件源等,以便进行调查和审计。
104 | - 安全审计:建立安全审计日志,记录关键事件和安全违规行为。
105 | - 有效期:采取监测、记录网络运行状态、网络安全事件的技术措施,并按照规定留存相关的网络日志不少于六个月;
106 |
107 |
108 |
109 | ## 2. C/S交互规范
110 |
111 | ### 2.1 服务端请求处理规范
112 |
113 | 1. 优先启动HTTPS访问,防止`中间人劫持攻击`;
114 | 2. 明确请求方式,原则上禁用`@RequestMapping`,改为具体的请求方式,如:`@GetMapping、@PostMapping`,防止`参数污染和请求方式不当风险`;
115 | 3. 合理的权限访问控制,API接口访问应做好严格的权限校验,防止`越权攻击`;
116 | 4. 重要业务需访问频率、次数限制,防止`数据泄露和密码爆破`;
117 | 5. 核心业务需限制访问IP;
118 | 6. 重要数据访问必须加密,如:账号、密码、手机号,防止`数据泄露`;
119 | 7. API请求参数应当添加CRC校验,保护数据完整性、防止`中间人劫持和请求重放攻击`;
120 | 8. 减少使用弱加密算法:`DES、RC4、MD5、SHA-1`,如:`MD5`加密时必须加上随机的`SALT`;推荐:`RSA、AES、SM(国密)`,请勿将:`Hex、URL、Base64`等编码方式当做加密算法;
121 | 9. 良好的数据格式校验,使用内置的验证框架(如:`Hibernate Validator`)来验证输入数据;
122 | 10. 正确使用`正则表达式`,防止`检测绕过回溯攻击`;
123 | 11. 生产环境应做好敏感信息保护减少暴露面,如:禁用`Swagger-UI、ElasticSearch、Weblogic/WebSphere/Tomcat/TongWeb控制台`等对外访问;
124 | 12. 启用自定义的`404、500`页面,禁止直接输出堆栈信息,防止`敏感信息泄露`;
125 | 13. 慎用或不用高风险组件解析请求参数,如:`Fastjson、XMLDecoder、XStream`;
126 | 14. 解析XML参数时务必禁用外部实体解析,防止XXE攻击;
127 | 15. 请求参数禁止当做`SpEL、Ognl、MVEL2、EL、JavaScript、Groovy、SQL`表达式或脚本执行;
128 | 16. 原则上禁止对请求参数进行Java对象反序列化,防止`Shiro、Apereo-CAS`之类的`Java反序列化漏洞`;
129 | 17. 文件上传请求中应严格检测文件名、文件内容是否合法,防止`文件上传漏洞`;
130 | 18. 文件上传的文件建议按照时间或者UUID的生成规则重命名,禁止原样存储,防止`文件上传漏洞`;
131 | 19. Session有效期不宜过长,尽量保持在30分钟以内,防止`会话固定攻击`;
132 | 20. 图形验证码每次校验完成后不管是否正确都必须清除与之对应的缓存,防止`验证码绕过漏洞`;
133 | 21. 慎用Spring MVC的请求参数对象绑定,防止参数污染;
134 |
135 |
136 |
137 | ### 2.2 HTTP响应头
138 |
139 | 响应中必须包含的响应头:
140 |
141 | ```yaml
142 | X-Content-Type-Options: nosniff
143 | X-Frame-Options: DENY
144 | ```
145 |
146 | **建议添加的响应头:**
147 |
148 | 1. **Content-Security-Policy (CSP)**:指定哪些内容可以加载到页面中,以防止跨站点脚本攻击(XSS)和其他恶意内容的注入。CSP 可以根据应用程序的需求进行定制。
149 | 2. **Strict-Transport-Security (HSTS)**:启用 HTTP 严格传输安全,强制使用 HTTPS 连接,并防止中间人攻击。
150 | 3. **X-Content-Security-Policy**:与 CSP 类似,但在较旧的浏览器中使用。不过,现代浏览器更倾向于使用 CSP。
151 | 4. **Referrer-Policy**:控制浏览器如何在请求头中包含 Referer(来源)信息。可以设置为 `strict-origin-when-cross-origin`或`no-referrer-when-downgrade`,以减少跨站点信息泄露。
152 | 5. **Server**:隐藏服务器信息,以减少攻击者获取有关服务器的信息的机会。
153 | 6. **Content-Type**:确保正确设置响应的 `Content-Type` 头,以避免浏览器执行不安全的默认操作,例如将 text文件当作可执行 html执行。
154 | 7. **Access-Control-Allow-Origin**:这是最常见的跨域响应头,用于指定允许访问资源的域名。可以设置为具体的域名或使用通配符 `*` 表示允许任何域名访问。
155 |
156 |
157 |
158 | ### 2.3 HTTP响应规范
159 |
160 | 1. 禁止未经过滤直接输出任何请求头,防止XSS攻击;
161 | 2. 禁止未经过滤直接输出任何请求参数,防止XSS攻击;
162 | 3. 响应头中包含`Location`时应当检查重定向的地址是否由用户输入,防止XSS攻击;
163 | 4. 响应头的值中应禁止换行符,防止CRLF攻击;
164 | 5. 明确MIME 类型,响应主体应指定正确的 MIME 类型(媒体类型),以告知客户端如何解释和处理响应内容。例如,HTML 内容应使用 `text/html`,JSON 数据应使用 `application/json`,图像应使用适当的图像 MIME 类型,防止XSS攻击;
165 | 6. 敏感信息应做好脱敏处理;
166 |
167 |
168 |
169 | ## 2.4 Session/Cookie规范
170 |
171 | ### 2.4.1 Session安全
172 |
173 | 1. Session必须设置有效期,建议保持30分钟以内,原则上最长不得超过1小时;
174 | 2. Session数据应该做到安全存储,如:内存、数据库或加密的持久性存储中,禁止将会话Session存储在客户端,以减少被窃取的风险;
175 |
176 |
177 |
178 | ### 2.4.2 Cookie安全
179 |
180 | 1. Cookie存储重要凭证相关的Cookie,建议开启`HttpOnly`和`Secure`机制;
181 |
182 | ```java
183 | Cookie myCookie = new Cookie("key", "value");
184 |
185 | // 设置 HttpOnly 标志
186 | myCookie.setHttpOnly(true);
187 |
188 | // 设置 Secure 标志(仅在HTTPS连接中传输)
189 | myCookie.setSecure(true);
190 | ```
191 |
192 | 2. 请勿滥用Cookie,存储于Cookie中的数据在客户端都有可能被恶意篡改,因此误将Cookie当Session使用,例如:
193 | - 某业务将找回密码步骤中是否通过邮件验证的标识存储于Cookie当中,服务端没有做二次校验,从而导致了攻击者只需修改Cookie中的标识即可绕过邮件认证;
194 | - 将用户密码、图形验证码存储在Cookie中导致敏感信息泄露和验证码校验绕过漏洞;
195 | 3. 合理设置Cookie的`Domain`、`Path`,防止Cookie信息泄露;
196 | 4. 做好客户端和服务端的XSS防御;
197 | - 服务端任何来源于请求参数的值输出到客户端并明确将用于HTML渲染时都应当使对输出内容进行HTML实体化;
198 | - 客户端应减少拼接HTML片段,或对拼接部分转义、过滤;
199 | 5. 如将Cookie用于广告、商品营销或其他涉及到Cookie追踪的场景时应遵循隐私政策,声明Cookie业务范围并需用户授权;
200 |
201 |
202 |
203 | ## 3. 编码/加密规范
204 |
205 | ### 3.1 加密算法强度
206 |
207 | 1. 少用或不用弱加密算法:`DES、RC4、MD5、SHA-1`;
208 | 2. `Hex、URLEncoder、Base64`是编码,不是加密算法,请勿滥用!
209 | 3. 选择强加密算法,如:RSA、AES、SM(国密);
210 | 4. 密钥长度需符合安全规范,例如,使用RSA 2048而不是RSA1024;
211 |
212 |
213 |
214 | ### 3.2 密钥存储规范
215 |
216 | 1. 禁止硬编码,避免将密钥硬编码到应用程序代码中,因为容易攻击者发现;
217 | 2. 严格控制密钥访问权限,避免直接存储于缓存服务中,可使用访问控制列表(ACLs)或身份验证和授权来限制访问;
218 | 3. 强密码保护,如果密钥需要密码,确保使用足够强的密码,并定期更改密码;
219 | 4. 定期轮换密钥,以减少密钥泄露或滥用的风险;
220 | 5. 定期备份存储的密钥,以防止丢失或损坏,备份应存储在安全的位置;
221 | 6. 禁止将密钥直接存储于Git或其他版本管理工具中,尤其是Github、Gitee等开源平台;
222 | 7. 使用密钥管理服务, 如果可能的话,使用专门的密钥管理服务(Key Management Service,KMS)来生成、存储和管理密钥。云服务提供商通常提供了这样的服务,例如AWS Key Management Service(KMS)或Google Cloud Key Management Service;
223 |
224 |
225 |
226 | ## 4. SQL查询规范
227 |
228 | SQL注入(`SQL injection`)是因为`应用程序`在执行SQL语句的时候没有正确的处理用户输入字符串,将用户输入的恶意字符串拼接到了SQL语句中执行,从而导致了SQL注入。
229 |
230 |
231 |
232 | ### 4.1 Spring JdbcTemplate
233 |
234 | #### 4.1.1 字符型
235 |
236 | 原则上禁止在SQL语句中直接拼接外部传入的字符串,因为攻击者可以通过SQL攻击闭合原始的SQL语义从而产生新的SQL查询,可导致数据库信息泄露、服务器被非法入侵等高危风险!
237 |
238 | **反例 - Spring JdbcTemplate SQL注入**
239 |
240 | ```java
241 | public Map getSysUserByUsername(String username) {
242 | String sql = "select * from sys_user where username = '" + username + "'";
243 |
244 | return jdbcTemplate.queryForMap(sql);
245 | }
246 | ```
247 |
248 | **示例 - Spring JdbcTemplate 预编译查询**
249 |
250 | ```java
251 | public Map findByUsername(String username) {
252 | String sql = "select * from sys_user where username = ? ";
253 |
254 | return jdbcTemplate.queryForMap(sql, username);
255 | }
256 | ```
257 |
258 | **示例 - SQL注入攻击**
259 |
260 | ```sql
261 | http://localhost:8080/SQL/Spring/jdbcTemplateStringInjection.do?username=admin' and 1=2 union select 1,2,sqlite_version(),4,5,6,'7
262 | ```
263 |
264 | SQL注入攻击执行结果:
265 |
266 | ```json
267 | {
268 | "id": 1,
269 | "username": 2,
270 | "password": "3.34.0",
271 | "email": 4,
272 | "user_avatar": 5,
273 | "register_time": 6,
274 | "notes": "7"
275 | }
276 | ```
277 |
278 | 攻击者使用前后的单引号闭合了原始SQL语句,并通过添加 `and 1=2` 让原SQL语句查询空数据。然后在UNION子查询中,攻击者使用了 `sqlite_version()` 函数来探测SQLite数据库引擎的版本号(其中`3.34.0`就是服务器端使用的Sqlite的版本号)。这个过程是一种信息搜集攻击,目的是帮助攻击者更好地了解目标系统的配置和弱点。
279 |
280 | 此外,攻击者也可能构建其他类型的SQL语句,以获取服务器中的敏感信息。例如,使用数据库提供的文件读写函数(如:Mysql的`load_file、into outfile`)或执行系统命令的函数(如:SQLServer的`xp_cmdshell`,MySQL的UDF)来直接获取数据库服务器的权限。
281 |
282 |
283 |
284 | #### 4.1.2 模糊查询
285 |
286 | **反例 - Spring JdbcTemplate 模糊查询SQL注入**
287 |
288 | ```java
289 | @GetMapping("/Spring/jdbcTemplateLikeInjection.do")
290 | public List