2017년 11월 24일 금요일

리눅스의 timestamp

리눅스의 timestamp는 3가지가 있다.

예전 글에도 ctime과 mtime의 차이점에 대해 썼었는데, 그에 대한 연장선이다.

Access(atime) - 파일을 마지막으로 읽은 시간
Modify(mtime) - 파일 내용이 마지막으로 바뀐 시간
Change(ctime) - 파일의 메타 데이터가 마지막 으로 변경된 시간 (e.g. 권한)

 

stat 명령어로 timestamp를 확인 할 수 있다.

toucth 명령어로 test.txt 파일을 하나 생성 후 확인
[root@df test]# touch test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:36:11.380000000 +0900
Modify: 2017-11-23 17:36:11.380000000 +0900
Change: 2017-11-23 17:36:11.380000000 +0900
Birth: -

처음 생성시 Access, Modify, Change가 모두 동일 한 걸 볼 수 있다.

 

touch 명령어로 Access 수정 후 확인.
[root@df test]# touch -a -d '1 Jan 2000 12:34' test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2017-11-23 17:36:11.380000000 +0900
Change: 2017-11-23 17:39:18.467000000 +0900
Birth: -

 

touch 명령어로 Modify 수정 후 확인.
[root@df test]# touch -m -d '1 Jan 2006 12:34' test.txt
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:39:52.616000000 +0900
Birth: -

 

권한 변경후 확인(Change)
[root@df test]# chmod 777 test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2000-01-01 12:34:00.000000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:40:18.557000000 +0900
Birth: -

 

cat 명령어로 해당 파일을 읽어 확인.
[root@df test]# cat test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 0 Blocks: 0 IO Block: 4096 일반 빈 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:48:21.985000000 +0900
Modify: 2006-01-01 12:34:00.000000000 +0900
Change: 2017-11-23 17:40:18.557000000 +0900
Birth: -

Access 만 변경 된 걸 볼 수 있다.

 

echo 명령어로 내용 추가 후 확인.
[root@df test]# echo test >> test.txt 
[root@df test]# stat test.txt
File: `test.txt'
Size: 5 Blocks: 8 IO Block: 4096 일반 파일
Device: fd00h/64768d Inode: 68752651 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-11-23 17:48:21.985000000 +0900
Modify: 2017-11-23 17:50:20.241000000 +0900
Change: 2017-11-23 17:50:20.241000000 +0900
Birth: -

Access는 그대로, Modify와 Change가 같이 변했다.

 

그니까 find로 파일 내용이 변경된 파일을 찾으려면 mtime을 이용해야 한다.
# 수정한지 20일 이상( -mtime +20 )된 파일과 디렉토리
find . -mtime +20 -ls

2017년 11월 21일 화요일

공격 아이피 iptable 등록 스크립트.

mail log를 점검하다 보니, 지속적으로 접근시도하는 부분이 보인다.

보안장비를 사용하면 간단하게 막으련만... 여의치 않아 스크립트를 작성하였다.
#/bin/bash


help()
{
echo ""
echo "/var/log/maillog is default"
echo "iptable apply is not default"
echo "banCount 100 is default"
echo "-a : iptable applied"
echo "-f : maillog FILE PATH"
echo "-c : ban count"
echo ""
echo "Usae : $0 -a -c 100 -f /var/log/maillog "
exit 0
}

ignoreIPs=("111.222.333.444" "111.111.111.111")
maillog="/var/log/maillog"
banCount=100
fw=false

# 옵션이름 뒤에 :이 붙은 것은 값을 필요로 함을 의미합니다.
while getopts ac:f: opt
do
case $opt in
a)
fw=true
;;
c)
banCount=$OPTARG
;;
f)
maillog=$OPTARG
;;
*)
help
exit 0
;;
esac
done


banIPs=`grep -E 'user not found|password fail' ${maillog} | awk -F: '{print $NF}' | sort | uniq -dc | awk '{if ($1 > '"${banCount}"') print $NF}'`

for banIP in $banIPs
do
for ignoreIP in ${ignoreIPs[@]}
do
if [ $ignoreIP == $banIP ]
then
# echo "ignore IP: ${ignoreIP}"
# echo "ban IP : $banIP"
break
fi
done

echo "ban IP : $banIP ,whois : $(geoiplookup $banIP)"
if [ $fw = true ] ; then
iptables -A INPUT -s ${banIP}/24 -j DROP
fi

done


if [ $fw = true ] ; then
#remove duplicate iptables rules
#http://www.krazyworks.com/remove-duplicate-iptables-rules/
/sbin/service iptables save
/sbin/iptables-save | awk '!x[$0]++' > /tmp/iptables.conf
/sbin/iptables -F
/sbin/iptables-restore < /tmp/iptables.conf
/sbin/service iptables save
/sbin/service iptables restart
if [ -f /tmp/iptables.conf ] ; then /bin/rm -f /tmp/iptables.conf ; fi
fi

qmail 큐(queue) 체크 스크립트.

계정의 패스워드가 노출되었거나, 사용자의 pc가 해킹 당했을 경우
해당 계정을 통해서 스팸 메일 발송이 자주 일어난다.

이렇게 되면 큐가 1000개는 기본으로 넘어가게 되어 정상적인 메일도
같이 안나게 된다.

정상적인 메일서버면 큐개수가 많아야 20~30개이므로
큐가 100개를 넘어가면 경고 메일을 보내도록 하였다.

아래 스크립트는 큐메일 용도이다.
#!/bin/sh
# Send an email when there are more then 1000 messages in the mail queue
# This is counted by the amount of lines in the qmail-qread output, so it's an indication...

show=$1
qread="/var/qmail/bin/qmail-qread"
qreadIDs=`/var/qmail/bin/qmail-qread | awk '{print $6}' | sed 's/#//' | grep -v '^$' | uniq`
len=`$qread | wc -l`
SUBJECT="WARNING: There are $len messages in the mail queue!----Country Check Version"
EMAIL="your@mail.com"
EMAILMESSAGE="/tmp/emailmessage.txt"
echo "" > $EMAILMESSAGE
chmod 777 $EMAILMESSAGE


if [ $len -gt 100 ]; then

for id in $qreadIDs
do
case "$id" in
"0.0.0.0"|"127.0.0.1"|"8.8.8.8")
continue;;
*)
#find /var/qmail/queue/mess/ -name 1449183 | xargs cat | grep mysolution-remoteip | awk '{print $2}' | xargs geoiplookup
queueFile=`find /var/qmail/queue/mess/ -name $id`
IP=`cat $queueFile | grep mysolution-remoteip | awk '{print $2}'`
# FROM=`cat $queueFile | grep From:`
# Subject=`cat $queueFile | grep Subject:`
countryChk=`geoiplookup $IP`
receiveChk=`cat $queueFile | grep Received:`

echo "Queue ID : $id --------------" >> $EMAILMESSAGE
echo "$IP : $countryChk" >> $EMAILMESSAGE
echo $FROM >> $EMAILMESSAGE
echo $Subject >> $EMAILMESSAGE
echo "$receiveChk" >> $EMAILMESSAGE
echo "-----------------------------" >> $EMAILMESSAGE
echo "" >> $EMAILMESSAGE

if [ 1 -eq $# ]; then

if [ $show = "show" ]; then
cat $EMAILMESSAGE
fi
fi
esac
done

MESSAGE=$(cat $EMAILMESSAGE)
# send an email using /bin/mail
#mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
printf "To: ${EMAIL}\nSubject: ${SUBJECT}\n\n $MESSAGE" | /var/qmail/bin/qmail-inject
rm -rf $EMAILMESSAGE

fi

2017년 11월 16일 목요일

mybatis hashmap으로 값 가져오기.

한개의 필드의 값을 hashmap으로 가져오려고 한다.

DB 내용
key1:value1,key2:value2,key3:value3

List<String> 으로 가져온 것과 마찬가지로 typeHandler를 사용하여 처리하였다.

 

DTO
HashMap<String, Object> extraProperties;


public HashMap<String, Object> getExtraProperties() {
return extraProperties;
}

public void setExtraProperties(HashMap<String, Object> extraProperties) {
this.extraProperties = extraProperties;
}

 

mybatis resultmap
<result property="extraProperties" column="extraProperties" typeHandler="com.my.handler.StringSplitMapTypeHandler" />

 

handler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.my.util.CommonUtil;

public class StringSplitMapTypeHandler implements TypeHandler<HashMap<String,Object>> {

@Override
public void setParameter(PreparedStatement ps, int i, HashMap<String,Object> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public HashMap<String,Object> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");

}

@Override
public HashMap<String,Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}

@Override
public HashMap<String,Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}
}

 

getStringSplitHashMap 코드
	public static HashMap<String,Object> getStringSplitHashMap(String targetStr, String fieldSeparator, String mapSeparator){
if (targetStr == null) return null;
String[] fields = targetStr.split(fieldSeparator);
HashMap<String,Object> returnData = new HashMap<String,Object>();
for(String str : fields){
String[] splitStr = str.split(mapSeparator);
if(splitStr.length < 2) continue;
String key = splitStr[0];
String value = splitStr[1];
returnData.put(key, value);
}
return returnData;
}

mybatis List으로 값 가져오기.

DB 테이블 한개의 필드에 저장된 값을 구분자로 나눠서 list로 가져오려고 한다.

typeHandler를 써서 간단히 적용 완료.

DB 내용 searchTags 필드
value1, value2, value3

,를 구분자로 각각의 value들이 하나의 List 값이 된다.

DTO
List<String> searchTags;

public List<String> getSearchTags() {
return searchTags;
}
public void setSearchTags(List<String> searchTags) {
this.searchTags = searchTags;
}

 

mybatis resultmap
<result property="searchTags" column="searchTags" typeHandler="com.my.handler.SplitTypeHandler" />

 

StringSplitTypeHandler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class SplitTypeHandler implements TypeHandler<List<String>> {

@Override
public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}
}

 

 

 

2017년 11월 14일 화요일

자바 json 데이터 정렬(?) 예쁘게(?) 출력하기.

GSON 사용시.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(uglyJSONString);
String prettyJsonString = gson.toJson(je);

 

jettison 사용시
JSONObject json = new JSONObject(jsonString); // Convert text to object
System.out.println(json.toString(4)); // Print it with specified indentation

 

jackson 사용시
String test = "{\"age\":29,\"messages\":[\"msg 1\",\"msg 2\",\"msg 3\"],\"name\":\"mkyong\"}";
Object json = mapper.readValue(test, Object.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));

2017년 11월 3일 금요일

casperjs 팁

casperjs 로 대여섯개 사이트 자동 출첵을 걸어놨다.

삽질 중에 얻은 팁을 공유한다.

 

1. <input> 에 type 이 없을 경우 fill 이 안 먹힘. << 이런 개 같은 경우가 있음  그럴 때는 sendKey를 활용.

2. alert 한글이 깨지는 경우 setting 옵션에 encoding: "utf8", 를 넣으면 됨.

3. verbose: true로 해야 syntax 에러 나옴.

4.onsubmit="INVEN.Outlogin.login(this); return false;"   << 이런식으로 되어 있으면 fill true 가 제대로 동작 안함. click 이벤트로 처리 하도록

(INVEN이네..;;)

 

11번가 처리시
5. iframe 처리iframe접근은 casper.withFrame 를 사용.

 
==================
[debug] [phantom] opening url: http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, HTTP GET
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, type=Other, willNavigate=true, isMainFrame=true << isMainFrame이 true
[debug] [phantom] url changed to "http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965"
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/html/blank.html, type=Other, willNavigate=true, isMainFrame=false << isMainFrame이 false임. iframe이란 소리.
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/jsp/event15/150625_benefitZoneAttend/iframe.jsp, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=about:blank, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=https://vars.hotjar.com/rcj-99d43ead6bdf30da8ed5ffcb4f17100c.html, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Successfully injected Casper client-side utilities
[info] [phantom] Step anonymous 5/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step anonymous 5/8: done in 6937ms.
[info] [phantom] Step _step 6/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step _step 6/8: done in 6955ms.
==================

위의 로그를 보면 iframe이 4개임.
iframe 이름이 있으면 casper.withFrame('이름'... 이런식으로 주지만, 이름이 없으면 0부터 index를 준다.