2018년 4월 21일 토요일

게시판 스크립트 방지 관련

포폴로 제출한 홈페이지를 잠깐 들어가봤는데 내가 설정하지 않은 alert창이 떠서 당황하였다.
인사담당자님께서 테스트하신 것같았는데 글 제목들은 공백이었고 내가 입력한적이 없는 alert창이 자꾸 발생하였다. 
어디서 자꾸 발생하나 개발자도구로 보니.....글 제목을 스크립트로 입력하신거였다...

<script>alert('Popup');</script>

전혀 생각하진 못한 문제였다...ㅠㅠㅠ

글 내용부분은 네이버 스마트에디터를 사용해서 스크립트가 방지되는 것같았다. 
글 제목엔 <script>alert('Popup');</script>을 써서 저장하면 해당게시판의 페이지에 접속할 때마다 알람이 발생한다!!! 


* XXS(Cross-site Scripting란?
SQL injection과 함께 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며, 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.

크로스 사이트 스크립팅이란 이름 답게, 자바스크립트를 사용하여 공격하는 경우가 많다. 공격 방법이 단순하고 가장 기초적이지만, 많은 웹사이트들이 XSS에 대한 방어 조치를 해두지 않아 공격을 받는 경우가 많다. 여러 사용자가 접근 가능한 게시판 등에 코드를 삽입하는 경우도 많으며, 경우에 따라서는 메일과 같은 매체를 통해서도 전파된다. 

물론, HTML을 사용하는 것이기 때문에, Text-Only 게시판이나, BBCode를 이용하는 위키위키 등에서는 XSS가 발생할 일은 없다. 단, 나무위키의 경우 {{{#!html HTML}}} 을 이용해서 HTML 태그를 사용할 수 있으므로 취약점이 있을 수도 있다. 그래서 나무위키 초반에는 스크립트 태그와 이벤트 속성도 막지 않았다. 물론, 이는 후에 대부분 수정되었다.

주로 CSRF를 하기 위해서 사용되기 때문에 종종 CSRF와 혼동되는 경우가 있으나, XSS는 자바스크립트를 실행시키는 것이고, CSRF는 특정한 행동을 시키는 것이므로 다르다.




해결방법은 좀 더 찾아보고 적용해야할 듯...ㅠㅠ 


http://forest71.tistory.com/16 (게시판 스크립트 방지관련 글)

https://blog.naver.com/powerlee12/40051367646 (xss에 대해서)

https://namu.wiki/w/XSS

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
lucy xss servlet filter를 적용 테스트 중 ...

2018년 3월 24일 토요일

modelandview란?

ModelAndView

org.springframework.web.servelt 패키지에 속해있는 클래스로 컨트롤러의 처리결과를 보여줄 뷰와 전달할 값을 저장할 용도로 쓰인다.
ModelAndView클래스를 보면 다음과 같이 구성되어 있다.
package org.springframework.web.servlet;

import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;

public class ModelAndView {
    private Object view;
    private ModelMap model;
    private HttpStatus status;
    private boolean cleared = false;

    public ModelAndView() {
    }

    public ModelAndView(String viewName) {
        this.view = viewName;
    }

    public ModelAndView(View view) {
        this.view = view;
    }

    public ModelAndView(String viewName, Map<String, ?> model) {
        this.view = viewName;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

    }

    public ModelAndView(View view, Map<String, ?> model) {
        this.view = view;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

    }

    public ModelAndView(String viewName, HttpStatus status) {
        this.view = viewName;
        this.status = status;
    }

    public ModelAndView(String viewName, Map<String, ?> model, HttpStatus status) {
        this.view = viewName;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

        this.status = status;
    }

    public ModelAndView(String viewName, String modelName, Object modelObject) {
        this.view = viewName;
        this.addObject(modelName, modelObject);
    }

    public ModelAndView(View view, String modelName, Object modelObject) {
        this.view = view;
        this.addObject(modelName, modelObject);
    }

    public void setViewName(String viewName) {
        this.view = viewName;
    }

    public String getViewName() {
        return this.view instanceof String ? (String)this.view : null;
    }

    public void setView(View view) {
        this.view = view;
    }

    public View getView() {
        return this.view instanceof View ? (View)this.view : null;
    }

    public boolean hasView() {
        return this.view != null;
    }

    public boolean isReference() {
        return this.view instanceof String;
    }

    protected Map<String, Object> getModelInternal() {
        return this.model;
    }

    public ModelMap getModelMap() {
        if (this.model == null) {
            this.model = new ModelMap();
        }

        return this.model;
    }

    public Map<String, Object> getModel() {
        return this.getModelMap();
    }

    public void setStatus(HttpStatus status) {
        this.status = status;
    }

    public HttpStatus getStatus() {
        return this.status;
    }

    public ModelAndView addObject(String attributeName, Object attributeValue) {
        this.getModelMap().addAttribute(attributeName, attributeValue);
        return this;
    }

    public ModelAndView addObject(Object attributeValue) {
        this.getModelMap().addAttribute(attributeValue);
        return this;
    }

    public ModelAndView addAllObjects(Map<String, ?> modelMap) {
        this.getModelMap().addAllAttributes(modelMap);
        return this;
    }

    public void clear() {
        this.view = null;
        this.model = null;
        this.cleared = true;
    }

    public boolean isEmpty() {
        return this.view == null && CollectionUtils.isEmpty(this.model);
    }

    public boolean wasCleared() {
        return this.cleared && this.isEmpty();
    }

    public String toString() {
        StringBuilder sb = new StringBuilder("ModelAndView: ");
        if (this.isReference()) {
            sb.append("reference to view with name '").append(this.view).append("'");
        } else {
            sb.append("materialized View is [").append(this.view).append(']');
        }

        sb.append("; model is ").append(this.model);
        return sb.toString();
    }
}
다음과 같이 map에 데이터를 담거나 직접 값을 전달해 줄 수있다.
@RequestMapping(value="list")
  public ModelAndView list(@RequestParam(defaultValue="title") String searchOption,
    @RequestParam(defaultValue="") String keyword,
    @RequestParam(defaultValue="1") int curPage) throws Exception{

  //전달할 정보 가져오기
  int count = adminService.countboard(searchOption, keyword);
  BoardPage boardPage = new BoardPage(count, curPage);
  int start = boardPage.getPageBegin();
  int end = boardPage.getPageEnd();
  List<WebBoard> list = adminService.Viewlist(start, end, searchOption, keyword);

  //전달할 정보 map에 담기
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("list", list);
  map.put("searchOption", searchOption);
  map.put("keyword", keyword);
  map.put("boardPage", boardPage);


  ModelAndView mav = new ModelAndView();

  //ModelAndView 객체에 전달할 정보(map)과 뷰 설정하기
  mav.addObject("map", map);
  //map에 넣지않고 직접 전달해도 된다.
  mav.addObject("count",count);
  mav.setViewName("/admin/adminmode");

  return mav;
  }
ModelAndView 외에도 다양하게 컨트롤러의 리턴타입을 정할 수 있음.
@ReqeustMapping("/list/")
  public String list(Model model){

    int count = adminService.countboard();

    model.addAttribute("count", count);

    return "/admin/adminmode"
  }

2018년 3월 4일 일요일

restcontroller

REST는 'Representational State Transfer' 의 약어로 하나의 URI는 하나의 고유한 Resource를 대표하도록
설계된다는 개념이다.
이 말은 다른 말로 'URI와 HTTP메소드를 이용해 객체화된 서비스에 접근한다고 말하는 것'이 라고 말할 수도 있는데
이 편이 더 쉽게 이해할 수도 있다.

REST API는 외부에서 특정 URI를 통해서 사용자가 원하는 정보를 제공하는 방식이다.
최근에 Open API에서 많이 사용되면서 REST 방식을 제공되는 외부 연결 URI를 REST API라고 하고,
REST 방식의 서비스 제공이 가능한 것을 'Restful' 하다고 표현한다.






예제( 다른 방법도 있는것같음 ) 
http://doublesprogramming.tistory.com/105

+++++

2018년 2월 8일 목요일

git branch 관련

브랜치란?
독립적인 여러 작업을 진행하기 위한 개념

여러 명이서 동시에 작업을 할 때에 다른 사람의 작업에 영향을 주거나 받지 않도록, 먼저 메인 브랜치에서 자신의 작업 전용 브랜치를 만듭니다. 그리고 각자 작업을 진행한 후, 작업이 끝난 사람은 메인 브랜치에 자신의 브랜치의 변경 사항을 적용합니다. 이렇게 함으로써 다른 사람의 작업에 영향을 받지 않고 독립적으로 특정 작업을 수행하고 그 결과를 하나로 모아 나가게 됩니다. 이러한 방식으로 작업할 경우 '작업 단위', 즉 브랜치로 그 작업의 기록을 중간 중간에 남기게 되므로 문제가 발생했을 경우 원인이 되는 작업을 찾아내거나 그에 따른 대책을 세우기 쉬워집니다.


>>결국 개발자들이 서로에 작업에 영향을 주지 않으면 독립된 공간에서 다양한 작업을 진행할 수 있게 도와주는 것 같음


master - 최종 배포용
develop - 개발용
feature - develop에서 새로운 기능을 만들때 쓰는 브랜치?

*************************************
출처
https://backlog.com/git-tutorial/kr/stepup/stepup1_1.html


브랜치 설명
https://mylko72.gitbooks.io/git/content/branch/branch_type.html




**********************************************
쓰는 흐름은
develop에서  feature를 따서 작은 기능을 개발 -> 작은 기능이 개발완료되면 develop으로 merge -> 반복 -> 모든 기능들이 완료되어 develop으로 합쳐지고, 정상적으로 동작하면 master에 올려서 최종배포

인듯 하다.

2018년 1월 21일 일요일

git에서 공유받은 maven dependencies / missing artifact 문제

http://parkpurong.tistory.com/133

깃으로 받은 프로젝트의 maven dependencies가 제대로 안받아지는것같다 ㅠ
위 블로그 방법으로해서 Missing artifact  는 없앴는데 계속안되서 그냥 내 로컬프로젝트 중 하나 pom.xml에서 버전 바꿔서 새버전 다시 받고 했음...

2017년 11월 29일 수요일

토비의 스프링 공부/정리 [3]

회사에서 토비의 스프링 스터디를 시작하였다.
새로운 기분으로 정독하고 있으며, 정리한 내용을 학습목적으로 git에 올리고 있다.
이해가 가지 않는 내용도 있지만, 일단 1회독을 마치는 것을 목표로 진행하자!









//////////////////////////////////////////////////////////////////////////////////////////////


3. 템플릿


템플릿이란 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며 일정한 패턴으로 유지되는 특성을 가진 부분을 자유롭게 변경되는 성질을 가진 부분으로 독립시켜서 효과적으로 활용할 수 있도록하는 방법. (??)
3장에선 스프링에 적용된 템플릿 기법을 살펴보고, 이를 적용해 완성된 DAO코드를 만드는 방법을 소개한다.


3.1 다시 보는 초난감 DAO

책에서 소개한 UserDao 코드에는 예외상황에 대한 처리 부분에 문제점이 있다고 한다.
preparedstatement를 처리하는 중 예외가 발생하면 close를 실행못하고 마치게된다. 이것이 계속쌓이면 리소스가 부족하다는 오류가 발생할 수 있다. 그래서 try/catch 구문으로 예외처리를 하도록 권장한다.
글 조회부분 또한 ResultSet도 반환해야하는 리소스이기 때문에 예외상황에서도 ResultSet의 close()메소드가 반드시 호출되도록 만들어줘야 한다.

리소스 반환과 close()

Connection이나 PreStatement에는 close()메소드가 있다. 종료라고 볼 수도 있지만 보통 리소스를 반환한다는 의미로 이해하는 것이 좋다. Connection과 PreparedStatement는 보통 풀(pool) 방식으로 운영된다. 미리 정해진 풀 안에 제한된 수의 리소스(Connection, Statement)를 만들어 두고 필요할 때 이를 할당하고, 반환하면 다시 풀에 넣는 방식으로 운영된다. 요청이 매우 많은 서버 환경에서는 매번 새로운 리소스를 생성하는 대신 풀에 미리 만들어둔 리소스를 돌려가며 사용하는 편이 훨씬 유리하다. 대신. 사용한 리소스는 빠르게반환해야 한다. 그렇지 안흥면 풀에 있는 리소스가 고갈되고 결국 문제가 발생한다. close()메소드는 사용한 리소스를 풀로 다시 돌려주는 역할을 한다.

3.2 변하는 것과 변하지 않는 것

finally 블록의 c.close() 라인 하나 빼먹은 것과 같은 실수를 했어도 테스트를 돌려보면 별 문제가 없어 보인다. 하지만 해당 메소드가 호출되고 나면 커넥션이 하나씩 반환되지 않고 쌓여가게 된다. 이를 그대로 사용하면 최대 DB커넥션 개수를 넘어설 것이고, 서버에서 리소스가 꽉 찼다는 에러가 나면서 서비스가 중단되는 상황이 발생한다.
>>이런 예외상황을 처리하는 코드는 테스트하기 매우 어렵고 번거롭다.
>> 효과적으로 다루기위해선..??

분리와 재사용을 위한 디자인 패턴 적용

책에서는 UserDao의 메소드를 개선하고 있다.
*****어려움 ㅠㅠ

3.3 JDBC 전략 패턴의 최적화


3.4 컨텍스트와 DI


3.5 템플릿과 콜백


3.6 스프링의 JdbcTemplate